From 4564d20dd9a7c4567eb524e851e6740e83cdabc7 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Wed, 20 Mar 2024 13:59:09 +0100 Subject: [PATCH 1/4] Improve Autostart handling by making sure agents and apps are automatically started once the auto start flag has ben set. --- .../AbstractExecutableBaseUnitController.java | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.java index 98d1d949ef..e79e1400e7 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -29,11 +29,13 @@ import org.openbase.bco.dal.lib.layer.service.provider.ActivationStateProviderService; import org.openbase.bco.dal.lib.state.States; import org.openbase.bco.dal.remote.action.RemoteAction; -import org.openbase.jul.exception.*; +import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InstantiationException; +import org.openbase.jul.exception.NotSupportedException; import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.exception.printer.LogLevel; import org.openbase.jul.iface.TimedProcessable; +import org.openbase.jul.schedule.CloseableWriteLockWrapper; import org.openbase.jul.schedule.FutureProcessor; import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; import org.openbase.type.domotic.action.ActionInitiatorType.ActionInitiator.InitiatorType; @@ -41,10 +43,11 @@ import org.openbase.type.domotic.action.ActionPriorityType.ActionPriority.Priority; import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType; import org.openbase.type.domotic.state.ActivationStateType.ActivationState; +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; + import java.io.Serializable; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; /** * @param the data type of this unit used for the state synchronization. @@ -69,20 +72,7 @@ public AbstractExecutableBaseUnitController(final DB builder) throws org.openbas @Override public void activate() throws CouldNotPerformException, InterruptedException { super.activate(); - try { - // setup autostart - if (detectAutostart()) { - final ActionParameter.Builder actionParameter = ActionDescriptionProcessor.generateDefaultActionParameter(States.Activation.ACTIVE, ServiceType.ACTIVATION_STATE_SERVICE, this); - actionParameter.setInterruptible(true); - actionParameter.setSchedulable(true); - actionParameter.setPriority(Priority.NO); - actionParameter.getActionInitiatorBuilder().setInitiatorType(InitiatorType.SYSTEM); - actionParameter.setExecutionTimePeriod(TimeUnit.MILLISECONDS.toMicros(TimedProcessable.INFINITY_TIMEOUT)); - new RemoteAction(applyAction(actionParameter), this::isActive); - } - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not autostart " + this, ex); - } + handleAutostart(); } @Override @@ -91,6 +81,15 @@ public void deactivate() throws CouldNotPerformException, InterruptedException { super.deactivate(); } + @Override + public UnitConfig applyConfigUpdate(final UnitConfig config) throws CouldNotPerformException, InterruptedException { + try (final CloseableWriteLockWrapper ignored = getManageWriteLockInterruptible(this)) { + var updatedConfig = super.applyConfigUpdate(config); + handleAutostart(); + return updatedConfig; + } + } + private boolean detectAutostart() { try { return isAutostartEnabled(); @@ -100,6 +99,25 @@ private boolean detectAutostart() { } } + private void handleAutostart() { + + if (!detectAutostart()) { + return; + } + + try { + final ActionParameter.Builder actionParameter = ActionDescriptionProcessor.generateDefaultActionParameter(States.Activation.ACTIVE, ServiceType.ACTIVATION_STATE_SERVICE, this); + actionParameter.setInterruptible(true); + actionParameter.setSchedulable(true); + actionParameter.setPriority(Priority.NO); + actionParameter.getActionInitiatorBuilder().setInitiatorType(InitiatorType.SYSTEM); + actionParameter.setExecutionTimePeriod(TimeUnit.MILLISECONDS.toMicros(TimedProcessable.INFINITY_TIMEOUT)); + new RemoteAction(applyAction(actionParameter), this::isActive); + } catch (CouldNotPerformException ex) { + ExceptionPrinter.printHistory("Could not autostart " + this, ex, logger, LogLevel.ERROR); + } + } + protected abstract boolean isAutostartEnabled() throws CouldNotPerformException; protected abstract ActionDescription execute(final ActivationState activationState) throws CouldNotPerformException, InterruptedException; From debc4d6fcb52899c8cca00b11258b4c1a44c98c0 Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Sun, 24 Mar 2024 20:53:26 +0100 Subject: [PATCH 2/4] port further stuff to kotlin and make sure activatable controller only activates in case the auto start has been changed during configuration update. --- .../app/cloudconnector/CloudConnectorApp.java | 14 +- .../InfluxDbconnectorApp.java | 516 ---------------- .../influxdbconnector/InfluxDbconnectorApp.kt | 560 ++++++++++++++++++ .../bco/app/preset/NightLightApp.java | 360 ----------- .../openbase/bco/app/preset/NightLightApp.kt | 347 +++++++++++ .../app/preset/PartyLightTileFollowerApp.java | 185 ------ .../app/preset/PartyLightTileFollowerApp.kt | 171 ++++++ .../AbstractExecutableBaseUnitController.java | 162 ----- .../AbstractExecutableBaseUnitController.kt | 146 +++++ .../layer/unit/AbstractUnitController.java | 2 +- .../unit/agent/AbstractAgentController.java | 60 -- .../unit/agent/AbstractAgentController.kt | 31 + .../agent/AgentControllerFactoryImpl.java | 102 ---- .../unit/agent/AgentControllerFactoryImpl.kt | 134 +++++ .../layer/unit/agent/AgentManagerImpl.java | 95 --- .../layer/unit/agent/AgentManagerImpl.kt | 71 +++ .../unit/agent/AgentManagerLauncher.java | 49 -- .../layer/unit/agent/AgentManagerLauncher.kt | 22 + .../layer/unit/app/AbstractAppController.java | 107 ---- .../layer/unit/app/AbstractAppController.kt | 63 ++ .../unit/app/AppControllerFactoryImpl.java | 96 --- .../unit/app/AppControllerFactoryImpl.kt | 196 ++++++ .../layer/unit/app/AppManagerImpl.java | 111 ---- .../control/layer/unit/app/AppManagerImpl.kt | 89 +++ .../layer/unit/app/AppManagerLauncher.java | 45 -- .../layer/unit/app/AppManagerLauncher.kt | 43 ++ .../lib/util/UnitConfigProcessor.java | 17 +- .../lib/auth/AuthorizationWithTokenHelper.kt | 12 +- 28 files changed, 1898 insertions(+), 1908 deletions(-) delete mode 100644 module/app/influxdbconnector/src/main/java/org/openbase/bco/app/influxdbconnector/InfluxDbconnectorApp.java create mode 100644 module/app/influxdbconnector/src/main/java/org/openbase/bco/app/influxdbconnector/InfluxDbconnectorApp.kt delete mode 100644 module/app/preset/src/main/java/org/openbase/bco/app/preset/NightLightApp.java create mode 100644 module/app/preset/src/main/java/org/openbase/bco/app/preset/NightLightApp.kt delete mode 100644 module/app/preset/src/main/java/org/openbase/bco/app/preset/PartyLightTileFollowerApp.java create mode 100644 module/app/preset/src/main/java/org/openbase/bco/app/preset/PartyLightTileFollowerApp.kt delete mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.java create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.kt delete mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AbstractAgentController.java create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AbstractAgentController.kt delete mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.java create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.kt delete mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerImpl.java create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerImpl.kt delete mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerLauncher.java create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerLauncher.kt delete mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AbstractAppController.java create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AbstractAppController.kt delete mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.kt delete mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerImpl.java create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerImpl.kt delete mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.java create mode 100644 module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.kt diff --git a/module/app/cloudconnector/src/main/java/org/openbase/bco/app/cloudconnector/CloudConnectorApp.java b/module/app/cloudconnector/src/main/java/org/openbase/bco/app/cloudconnector/CloudConnectorApp.java index 77f7bdd97b..7ae502369c 100644 --- a/module/app/cloudconnector/src/main/java/org/openbase/bco/app/cloudconnector/CloudConnectorApp.java +++ b/module/app/cloudconnector/src/main/java/org/openbase/bco/app/cloudconnector/CloudConnectorApp.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -34,7 +34,6 @@ import org.openbase.jul.communication.iface.RPCServer; import org.openbase.jul.exception.CouldNotPerformException; import org.openbase.jul.exception.InitializationException; -import org.openbase.jul.exception.InstantiationException; import org.openbase.jul.exception.NotAvailableException; import org.openbase.jul.extension.type.processing.MetaConfigPool; import org.openbase.jul.extension.type.processing.MetaConfigVariableProvider; @@ -65,14 +64,12 @@ public class CloudConnectorApp extends AbstractAppController implements CloudCon private static final Logger LOGGER = LoggerFactory.getLogger(CloudConnectorApp.class); private final CloudConnectorTokenStore tokenStore; - private final JsonParser jsonParser; private final Map userIdSocketMap; - public CloudConnectorApp() throws InstantiationException { + public CloudConnectorApp() { super(); this.userIdSocketMap = new HashMap<>(); this.tokenStore = new CloudConnectorTokenStore(); - this.jsonParser = new JsonParser(); } @Override @@ -235,7 +232,8 @@ public Future connect(final AuthenticatedValue authenticated socketWrapper.activate(); socketWrapper.getLoginFuture().get(10, TimeUnit.SECONDS); } - } catch (CouldNotPerformException | ExecutionException | InterruptedException | TimeoutException ex) { + } catch (CouldNotPerformException | ExecutionException | InterruptedException | + TimeoutException ex) { if (ex instanceof InterruptedException) { Thread.currentThread().interrupt(); } @@ -278,7 +276,7 @@ public Future register(AuthenticatedValue authenticatedValue try { // parse string as json - final JsonObject params = jsonParser.parse(jsonString).getAsJsonObject(); + final JsonObject params = JsonParser.parseString(jsonString).getAsJsonObject(); // validate that password hash is available if (!params.has(PASSWORD_HASH_KEY)) { diff --git a/module/app/influxdbconnector/src/main/java/org/openbase/bco/app/influxdbconnector/InfluxDbconnectorApp.java b/module/app/influxdbconnector/src/main/java/org/openbase/bco/app/influxdbconnector/InfluxDbconnectorApp.java deleted file mode 100644 index 343703718c..0000000000 --- a/module/app/influxdbconnector/src/main/java/org/openbase/bco/app/influxdbconnector/InfluxDbconnectorApp.java +++ /dev/null @@ -1,516 +0,0 @@ -package org.openbase.bco.app.influxdbconnector; - -/*- - * #%L - * BCO InfluxDB Connector - * %% - * Copyright (C) 2018 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - - -import com.google.protobuf.Descriptors; -import com.google.protobuf.Message; -import com.influxdb.client.InfluxDBClient; -import com.influxdb.client.InfluxDBClientFactory; -import com.influxdb.client.WriteApi; -import com.influxdb.client.WriteOptions; -import com.influxdb.client.domain.Bucket; -import com.influxdb.client.domain.WritePrecision; -import com.influxdb.client.write.Point; -import com.influxdb.client.write.events.WriteErrorEvent; -import com.influxdb.client.write.events.WriteSuccessEvent; -import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController; -import org.openbase.bco.dal.lib.layer.service.ServiceStateProvider; -import org.openbase.bco.dal.lib.layer.service.Services; -import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.bco.dal.lib.layer.unit.UnitRemote; -import org.openbase.bco.dal.remote.layer.unit.CustomUnitPool; -import org.openbase.bco.dal.remote.layer.unit.Units; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.jul.exception.*; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.exception.printer.LogLevel; -import org.openbase.jul.extension.type.processing.LabelProcessor; -import org.openbase.jul.extension.type.processing.TimestampProcessor; -import org.openbase.jul.pattern.Observer; -import org.openbase.jul.schedule.CloseableWriteLockWrapper; -import org.openbase.jul.schedule.GlobalCachedExecutorService; -import org.openbase.jul.schedule.GlobalScheduledExecutorService; -import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; -import org.openbase.type.domotic.service.ServiceDescriptionType; -import org.openbase.type.domotic.service.ServiceTemplateType; -import org.openbase.type.domotic.service.ServiceTempusTypeType; -import org.openbase.type.domotic.service.ServiceTempusTypeType.ServiceTempusType.ServiceTempus; -import org.openbase.type.domotic.state.ActivationStateType.ActivationState; -import org.openbase.type.domotic.unit.UnitConfigType; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.language.LabelType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.*; - -import static org.openbase.bco.dal.control.layer.unit.InfluxDbProcessor.*; -import static org.openbase.bco.dal.lib.layer.service.Services.resolveStateValue; - -public class InfluxDbconnectorApp extends AbstractAppController { - - private Logger logger = LoggerFactory.getLogger(getClass()); - - private WriteApi writeApi; - private Integer databaseTimeout = DATABASE_TIMEOUT_DEFAULT; - private Bucket bucket; - private char[] token; - private Future task; - private Future heartbeat; - private String databaseUrl; - private String bucketName; - private InfluxDBClient influxDBClient; - private Integer batchTime; - private Integer batchLimit; - private final CustomUnitPool customUnitPool; - private final Observer, Message> unitStateObserver; - private String org; - - - public InfluxDbconnectorApp() throws InstantiationException { - this.customUnitPool = new CustomUnitPool(); - this.unitStateObserver = (source, data) -> storeServiceState((Unit) source.getServiceProvider(), source.getServiceType(), false); - } - - @Override - public UnitConfig applyConfigUpdate(UnitConfig config) throws CouldNotPerformException, InterruptedException { - try (final CloseableWriteLockWrapper ignored = getManageWriteLockInterruptible(this)) { - config = super.applyConfigUpdate(config); - - bucketName = generateVariablePool().getValue(INFLUXDB_BUCKET, INFLUXDB_BUCKET_DEFAULT); - batchTime = Integer.valueOf(generateVariablePool().getValue(INFLUXDB_BATCH_TIME, INFLUXDB_BATCH_TIME_DEFAULT)); - batchLimit = Integer.valueOf(generateVariablePool().getValue(INFLUXDB_BATCH_LIMIT, INFLUXDB_BATCH_LIMIT_DEFAULT)); - databaseUrl = generateVariablePool().getValue(INFLUXDB_URL, INFLUXDB_URL_DEFAULT); - token = generateVariablePool().getValue(INFLUXDB_TOKEN).toCharArray(); - org = generateVariablePool().getValue(INFLUXDB_ORG, INFLUXDB_ORG_DEFAULT); - - return config; - } - } - - @Override - protected ActionDescription execute(ActivationState activationState) { - - task = GlobalCachedExecutorService.submit(() -> { - - try { - logger.debug("Execute influx db connector"); - - // connect to db - connectToDatabase(); - while (!task.isCancelled()) { - try { - verifyConnection(); - break; - - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not reach influxdb server at " + databaseUrl + ". Try again in " + databaseTimeout / 1000 + " seconds!", ex, logger, LogLevel.WARN); - Thread.sleep(databaseTimeout); - if (databaseTimeout < MAX_TIMEOUT) databaseTimeout += ADDITIONAL_TIMEOUT; - } - } - - // lookup bucked - while (!task.isCancelled()) { - try { - // check if bucked found - getDatabaseBucket(); - break; - - } catch (NotAvailableException ex) { - logger.warn("Could not get bucket. Try again in " + databaseTimeout / 1000 + " seconds!"); - - ExceptionPrinter.printHistory(ex, logger); - Thread.sleep(databaseTimeout); - } - } - - // start observation - try { - startObservation(); - } catch (InitializationException ex) { - ExceptionPrinter.printHistory(ex, logger); - } - } catch (InterruptedException ex) { - // finish task because its canceled. - } - - if (!task.isCancelled() && isConnected()) { - try { - // write initial heartbeat - logger.debug("initial heartbeat"); - writeApi.writePoint(bucketName, org, Point.measurement(HEARTBEAT_MEASUREMENT) - .addField(HEARTBEAT_FIELD, HEARTBEAT_OFFLINE_VALUE) - .time(System.currentTimeMillis() - 1, WritePrecision.MS)); - writeApi.writePoint(bucketName, org, Point.measurement(HEARTBEAT_MEASUREMENT) - .addField(HEARTBEAT_FIELD, HEARTBEAT_ONLINE_VALUE) - .time(System.currentTimeMillis(), WritePrecision.MS)); - - heartbeat = GlobalScheduledExecutorService.scheduleAtFixedRate(() -> { - logger.debug("write heartbeat"); - writeApi.writePoint(bucketName, org, Point.measurement(HEARTBEAT_MEASUREMENT) - .addField(HEARTBEAT_FIELD, HEARTBEAT_ONLINE_VALUE) - .time(System.currentTimeMillis(), WritePrecision.MS)); - - }, HEARTBEAT_INITIAL_DELAY, HEARTBEAT_PERIOD, TimeUnit.MILLISECONDS); - } catch (NotAvailableException ex) { - ExceptionPrinter.printHistory("Could not write heartbeat!", ex, logger, LogLevel.WARN); - } - } - return null; - }); - return activationState.getResponsibleAction(); - } - - @Override - protected void stop(ActivationState activationState) throws CouldNotPerformException, InterruptedException { - - // finish task - logger.debug("finish task"); - if (task != null && !task.isDone()) { - task.cancel(true); - try { - task.get(5, TimeUnit.SECONDS); - } catch (CancellationException ex) { - // that's what we are waiting for. - } catch (Exception ex) { - ExceptionPrinter.printHistory(ex, logger); - } - } - - logger.debug("finish heartbeat"); - if (heartbeat != null && !heartbeat.isDone()) { - heartbeat.cancel(true); - try { - task.get(5, TimeUnit.SECONDS); - } catch (CancellationException ex) { - // that's what we are waiting for. - } catch (Exception ex) { - ExceptionPrinter.printHistory(ex, logger); - } - } - - if (isConnected()) { - // write final heartbeat if connection is established. - writeApi.writePoint(bucketName, org, Point.measurement(HEARTBEAT_MEASUREMENT) - .addField(HEARTBEAT_FIELD, HEARTBEAT_ONLINE_VALUE) - .time(System.currentTimeMillis() - 1, WritePrecision.MS)); - writeApi.writePoint(bucketName, org, Point.measurement(HEARTBEAT_MEASUREMENT) - .addField(HEARTBEAT_FIELD, HEARTBEAT_OFFLINE_VALUE) - .time(System.currentTimeMillis(), WritePrecision.MS)); - writeApi.flush(); - } - - // deregister - customUnitPool.removeServiceStateObserver(unitStateObserver); - customUnitPool.deactivate(); - disconnectDatabase(); - - super.stop(activationState); - } - - public void startObservation() throws InitializationException, InterruptedException { - try { - // setup pool - customUnitPool.addServiceStateObserver(unitStateObserver); - customUnitPool.activate(); - - for (UnitConfigType.UnitConfig unitConfig : Registries.getUnitRegistry(true).getUnitConfigs()) { - final UnitRemote unit; - try { - unit = Units.getFutureUnit(unitConfig, true).get(MAX_INITIAL_STORAGE_TIMEOUT, TimeUnit.MILLISECONDS); - } catch (ExecutionException | TimeoutException ex) { - ExceptionPrinter.printHistory("Could not reach Unit " + LabelProcessor.getBestMatch(unitConfig.getLabel()) + "! Skip initial service state synchronisation because unit will be synchronized anyway when it connection is established.", ex, logger, LogLevel.DEBUG); - continue; - } - try { - for (ServiceDescriptionType.ServiceDescription serviceDescription : unit.getUnitTemplate().getServiceDescriptionList()) { - - if (serviceDescription.getPattern() != ServiceTemplateType.ServiceTemplate.ServicePattern.PROVIDER) { - continue; - } - storeServiceState(unit, serviceDescription.getServiceType(), true); - } - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not store service state " + unit, ex, logger); - } - } - } catch (CouldNotPerformException ex) { - throw new InitializationException(this, ex); - } - } - - private void storeServiceState(Unit unit, ServiceTemplateType.ServiceTemplate.ServiceType serviceType, boolean initialSync) throws CouldNotPerformException { - - final Message currentServiceState; - Message lastServiceState = null; - try { - currentServiceState = Services.invokeProviderServiceMethod(serviceType, ServiceTempus.CURRENT, unit.getData()); - } catch (NotAvailableException ex) { - // if the current state is not available, we just try to at least store the last known service state if available. - - try { - lastServiceState = Services.invokeProviderServiceMethod(serviceType, ServiceTempusTypeType.ServiceTempusType.ServiceTempus.LAST, unit.getData()); - storeServiceState(unit, serviceType, lastServiceState); - } catch (CouldNotPerformException exx) { - // we don't care if the last service state is not available - // which can be the case for an initial sync - // or any states which got only one state update since system startup. - } - return; - } - - // store t - 1 entry - try { - lastServiceState = Services.invokeProviderServiceMethod(serviceType, ServiceTempusTypeType.ServiceTempusType.ServiceTempus.LAST, unit.getData()); - final long serviceStateTimestamp = TimestampProcessor.getTimestamp(currentServiceState, TimeUnit.MILLISECONDS) - 1l; - lastServiceState = TimestampProcessor.updateTimestamp(serviceStateTimestamp, lastServiceState, TimeUnit.MILLISECONDS); - storeServiceState(unit, serviceType, lastServiceState); - } catch (CouldNotPerformException ex) { - // we don't care if the last service state is not available - // which can be the case for an initial sync - // or any states which got only one state update since system startup. - } - - try { - storeServiceState(unit, serviceType, currentServiceState); - } catch (CouldNotPerformException ex) { - - // filter log if initial timestamps are missing - if (initialSync) { - return; - } - - ExceptionPrinter.printHistory("Could not store service state change into db! " + - "UnitType[" + unit.getUnitType() + "] " + - "ServiceType[" + serviceType + "] " + - "CurrentServiceState[" + currentServiceState + "] " + - "LastServiceState[" + lastServiceState + "]" - , ex, logger, LogLevel.DEBUG); - } - } - - private void storeServiceState(final Unit unit, final ServiceTemplateType.ServiceTemplate.ServiceType serviceType, final Message serviceState) throws InvalidStateException { - - final long timestamp = TimestampProcessor.getTimestamp(serviceState, TimeUnit.MILLISECONDS); - try { - String initiator; - try { - initiator = Services.getResponsibleAction(serviceState).getActionInitiator().getInitiatorType().name().toLowerCase(); - } catch (NotAvailableException ex) { - // in this case we use the system as initiator because responsible actions are not available for pure provider services and those are always system generated. - initiator = "system"; - } - Map stateValuesMap = resolveStateValueToMap(serviceState); - Point point = Point.measurement(serviceType.name().toLowerCase()) - .addTag("alias", unit.getConfig().getAlias(0)) - .addTag("initiator", initiator) - .addTag("unit_id", unit.getId()) - .addTag("unit_type", unit.getUnitType().name().toLowerCase()) - .addTag("location_id", unit.getParentLocationConfig().getId()) - .addTag("location_alias", unit.getParentLocationConfig().getAlias(0)) - .time(timestamp, WritePrecision.MS); - - Integer values = 0; - for (Map.Entry entry : stateValuesMap.entrySet()) { - // detect numbers with regex - if (entry.getValue().matches("-?\\d+(\\.\\d+)?")) { - values++; - point.addField(entry.getKey(), Double.valueOf(entry.getValue())); - - } else { - point.addTag(entry.getKey(), entry.getValue()); - } - } - - List entryList = unit.getConfig().getLabel().getEntryList(); - for (LabelType.Label.MapFieldEntry entry : entryList) { - // skip entries that offer a language key but do not provide any label. - if(entry.getValueList().isEmpty()) { - continue; - } - point.addTag("label_" + entry.getKey(), entry.getValue(0)); - } - - if (values > 0) { - writeApi.writePoint(bucketName, org, point); - } - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not store service state " + serviceType.name() + " of " + unit, ex, logger); - } - } - - - public Map resolveStateValueToMap(Message serviceState) throws CouldNotPerformException { - final Map stateValues = new HashMap<>(); - for (Descriptors.FieldDescriptor fieldDescriptor : serviceState.getDescriptorForType().getFields()) { - String stateName = fieldDescriptor.getName(); - String stateType = fieldDescriptor.getType().toString().toLowerCase(); - - // filter invalid states - if (stateName == null || stateType == null) { - logger.warn("Could not detect datatype of " + stateName); - } - - // filter general service fields - switch (stateName) { - case "aggregated_value_coverage": - case "last_value_occurrence": - case "timestamp": - case "responsible_action": - case "type": - case "rgb_color": - case "frame_id": - continue; - } - - // filter data units - if (stateName.endsWith("data_unit")) { - continue; - } - - String stateValue = serviceState.getField(fieldDescriptor).toString(); - - try { - if (fieldDescriptor.getType() == Descriptors.FieldDescriptor.Type.MESSAGE) { - if (fieldDescriptor.isRepeated()) { - List types = new ArrayList<>(); - - for (int i = 0; i < serviceState.getRepeatedFieldCount(fieldDescriptor); i++) { - final Object repeatedFieldEntry = serviceState.getRepeatedField(fieldDescriptor, i); - if (repeatedFieldEntry instanceof Message) { - types.add("[" + resolveStateValue((Message) repeatedFieldEntry).toString() + "]"); - } - types.add(repeatedFieldEntry.toString()); - } - stateType = types.toString().toLowerCase(); - } else { - stateValue = resolveStateValue((Message) serviceState.getField(fieldDescriptor)).toString(); - } - } - } catch (InvalidStateException ex) { - logger.warn("Could not process value of " + fieldDescriptor.getName()); - continue; - } - - // filter values - switch (stateValue) { - case "": - case "NaN": - continue; - default: - break; - } - if (fieldDescriptor.getJavaType() == Descriptors.FieldDescriptor.JavaType.ENUM) { - String finalStateValue = stateValue; - stateValue = String.valueOf(fieldDescriptor.getEnumType().getValues().stream().filter(val -> val.getName().equals(finalStateValue)).findFirst().get().getNumber()); - } - - stateValues.put(fieldDescriptor.getName(), stateValue.toLowerCase()); - } - return stateValues; - } - - /** - * Method checks if the connection is established. - * - * @return true if the connection to influx db is established, otherwise false. - */ - private boolean isConnected() { - try { - verifyConnection(); - } catch (VerificationFailedException ex) { - return false; - } - return true; - } - - /** - * Method verifies the connection state. - * - * @throws VerificationFailedException is thrown if the connection is not established. - */ - private void verifyConnection() throws VerificationFailedException { - - if (influxDBClient == null) { - throw new VerificationFailedException("Influx db connection has never been initiated."); - } - - if (influxDBClient.health().getStatus().getValue() != "pass") { - throw new VerificationFailedException("Could not connect to database server at " + databaseUrl + "!"); - } - - // initiate WriteApi - WriteOptions writeoptions = WriteOptions.builder().batchSize(batchLimit).flushInterval(batchTime).build(); - writeApi = influxDBClient.getWriteApi(writeoptions); - writeApi.listenEvents(WriteSuccessEvent.class, event -> { - logger.debug("Successfully wrote data into db"); - }); - writeApi.listenEvents(WriteErrorEvent.class, event -> { - Throwable exception = event.getThrowable(); - logger.warn(exception.getMessage()); - }); - logger.debug("Connected to Influxdb at " + databaseUrl); - } - - private void connectToDatabase() { - try { - if (influxDBClient != null) { - influxDBClient.close(); - } - } catch (Exception ex) { - ExceptionPrinter.printHistory("Could not shutdown database connection!", ex, logger); - } - logger.debug(" Try to connect to influxDB at " + databaseUrl); - influxDBClient = InfluxDBClientFactory.create(databaseUrl + "?readTimeout=" + READ_TIMEOUT + "&connectTimeout=" + CONNECT_TIMOUT + "&writeTimeout=" + WRITE_TIMEOUT + "&logLevel=BASIC", token); - } - - private void disconnectDatabase() { - try { - if (influxDBClient != null) { - if (writeApi != null) { - writeApi.flush(); - } - influxDBClient.close(); - writeApi = null; - } - } catch (Exception ex) { - ExceptionPrinter.printHistory("Could not shutdown database connection!", ex, logger); - } - } - - private void getDatabaseBucket() throws NotAvailableException { - logger.debug("Get bucket " + bucketName); - bucket = influxDBClient.getBucketsApi().findBucketByName(bucketName); - if (bucket == null) { - throw new NotAvailableException("bucket", bucketName); - } - } -} diff --git a/module/app/influxdbconnector/src/main/java/org/openbase/bco/app/influxdbconnector/InfluxDbconnectorApp.kt b/module/app/influxdbconnector/src/main/java/org/openbase/bco/app/influxdbconnector/InfluxDbconnectorApp.kt new file mode 100644 index 0000000000..c4e1923a1a --- /dev/null +++ b/module/app/influxdbconnector/src/main/java/org/openbase/bco/app/influxdbconnector/InfluxDbconnectorApp.kt @@ -0,0 +1,560 @@ +package org.openbase.bco.app.influxdbconnector + +import com.google.protobuf.Descriptors +import com.google.protobuf.Message +import com.influxdb.client.InfluxDBClient +import com.influxdb.client.InfluxDBClientFactory +import com.influxdb.client.WriteApi +import com.influxdb.client.WriteOptions +import com.influxdb.client.domain.Bucket +import com.influxdb.client.domain.WritePrecision +import com.influxdb.client.write.Point +import com.influxdb.client.write.events.WriteErrorEvent +import com.influxdb.client.write.events.WriteSuccessEvent +import org.openbase.bco.dal.control.layer.unit.InfluxDbProcessor +import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController +import org.openbase.bco.dal.lib.layer.service.ServiceStateProvider +import org.openbase.bco.dal.lib.layer.service.Services +import org.openbase.bco.dal.lib.layer.unit.UnitRemote +import org.openbase.bco.dal.remote.layer.unit.CustomUnitPool +import org.openbase.bco.dal.remote.layer.unit.Units +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.* +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.exception.printer.LogLevel +import org.openbase.jul.extension.type.processing.LabelProcessor.getBestMatch +import org.openbase.jul.extension.type.processing.TimestampProcessor +import org.openbase.jul.pattern.Observer +import org.openbase.jul.schedule.GlobalCachedExecutorService +import org.openbase.jul.schedule.GlobalScheduledExecutorService +import org.openbase.type.domotic.action.ActionDescriptionType +import org.openbase.type.domotic.service.ServiceTemplateType +import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServicePattern +import org.openbase.type.domotic.service.ServiceTempusTypeType.ServiceTempusType.ServiceTempus +import org.openbase.type.domotic.state.ActivationStateType +import org.openbase.type.domotic.unit.UnitConfigType +import java.util.* +import java.util.concurrent.* +import java.util.concurrent.TimeoutException + + +class InfluxDbconnectorApp : AbstractAppController() { + private var writeApi: WriteApi? = null + private var databaseTimeout: Int = InfluxDbProcessor.DATABASE_TIMEOUT_DEFAULT + private var bucket: Bucket? = null + private var token: CharArray? = null + private var task: Future<*>? = null + private var heartbeat: Future<*>? = null + private var databaseUrl: String? = null + private var bucketName: String? = null + private var influxDBClient: InfluxDBClient? = null + private var batchTime: Int? = null + private var batchLimit: Int? = null + private val customUnitPool: CustomUnitPool<*, *> = CustomUnitPool>() + private val unitStateObserver: Observer, Message> + private var org: String? = null + + + init { + this.unitStateObserver = + Observer, Message> { source: ServiceStateProvider, data: Message -> + storeServiceState( + source.serviceProvider as org.openbase.bco.dal.lib.layer.unit.Unit<*>, + source.serviceType, + false + ) + } + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun applyConfigUpdate(config: UnitConfigType.UnitConfig): UnitConfigType.UnitConfig { + var config: UnitConfigType.UnitConfig? = config + getManageWriteLockInterruptible(this).use { ignored -> + config = super.applyConfigUpdate(config!!) + bucketName = generateVariablePool().getValue( + InfluxDbProcessor.INFLUXDB_BUCKET, + InfluxDbProcessor.INFLUXDB_BUCKET_DEFAULT + ) + batchTime = generateVariablePool().getValue( + InfluxDbProcessor.INFLUXDB_BATCH_TIME, + InfluxDbProcessor.INFLUXDB_BATCH_TIME_DEFAULT + ).toInt() + batchLimit = generateVariablePool().getValue( + InfluxDbProcessor.INFLUXDB_BATCH_LIMIT, + InfluxDbProcessor.INFLUXDB_BATCH_LIMIT_DEFAULT + ).toInt() + databaseUrl = + generateVariablePool().getValue(InfluxDbProcessor.INFLUXDB_URL, InfluxDbProcessor.INFLUXDB_URL_DEFAULT) + token = generateVariablePool().getValue(InfluxDbProcessor.INFLUXDB_TOKEN).toCharArray() + org = + generateVariablePool().getValue(InfluxDbProcessor.INFLUXDB_ORG, InfluxDbProcessor.INFLUXDB_ORG_DEFAULT) + return config!! + } + } + + override fun execute(activationState: ActivationStateType.ActivationState): ActionDescriptionType.ActionDescription? { + task = GlobalCachedExecutorService.submit { + try { + logger.debug("Execute influx db connector") + + // connect to db + connectToDatabase() + while (!task!!.isCancelled) { + try { + verifyConnection() + break + } catch (ex: CouldNotPerformException) { + ExceptionPrinter.printHistory( + "Could not reach influxdb server at " + databaseUrl + ". Try again in " + databaseTimeout / 1000 + " seconds!", + ex, + logger, + LogLevel.WARN + ) + Thread.sleep(databaseTimeout.toLong()) + if (databaseTimeout < InfluxDbProcessor.MAX_TIMEOUT) databaseTimeout += InfluxDbProcessor.ADDITIONAL_TIMEOUT + } + } + + // lookup bucked + while (!task!!.isCancelled) { + try { + // check if bucked found + databaseBucket + break + } catch (ex: NotAvailableException) { + logger.warn("Could not get bucket. Try again in " + databaseTimeout / 1000 + " seconds!") + + ExceptionPrinter.printHistory(ex, logger) + Thread.sleep(databaseTimeout.toLong()) + } + } + + // start observation + try { + startObservation() + } catch (ex: InitializationException) { + ExceptionPrinter.printHistory(ex, logger) + } + } catch (ex: InterruptedException) { + // finish task because its canceled. + } + if (!task!!.isCancelled && isConnected) { + try { + // write initial heartbeat + logger.debug("initial heartbeat") + writeApi!!.writePoint( + bucketName!!, org!!, Point.measurement(InfluxDbProcessor.HEARTBEAT_MEASUREMENT) + .addField(InfluxDbProcessor.HEARTBEAT_FIELD, InfluxDbProcessor.HEARTBEAT_OFFLINE_VALUE) + .time(System.currentTimeMillis() - 1, WritePrecision.MS) + ) + writeApi!!.writePoint( + bucketName!!, org!!, Point.measurement(InfluxDbProcessor.HEARTBEAT_MEASUREMENT) + .addField(InfluxDbProcessor.HEARTBEAT_FIELD, InfluxDbProcessor.HEARTBEAT_ONLINE_VALUE) + .time(System.currentTimeMillis(), WritePrecision.MS) + ) + + heartbeat = GlobalScheduledExecutorService.scheduleAtFixedRate( + { + logger.debug("write heartbeat") + writeApi!!.writePoint( + bucketName!!, org!!, Point.measurement(InfluxDbProcessor.HEARTBEAT_MEASUREMENT) + .addField( + InfluxDbProcessor.HEARTBEAT_FIELD, + InfluxDbProcessor.HEARTBEAT_ONLINE_VALUE + ) + .time(System.currentTimeMillis(), WritePrecision.MS) + ) + }, + InfluxDbProcessor.HEARTBEAT_INITIAL_DELAY.toLong(), + InfluxDbProcessor.HEARTBEAT_PERIOD, + TimeUnit.MILLISECONDS + ) + } catch (ex: NotAvailableException) { + ExceptionPrinter.printHistory( + "Could not write heartbeat!", + ex, + logger, + LogLevel.WARN + ) + } + } + null + } + return activationState.responsibleAction + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun stop(activationState: ActivationStateType.ActivationState) { + // finish task + + logger.debug("finish task") + if (task != null && !task!!.isDone) { + task!!.cancel(true) + try { + task!![5, TimeUnit.SECONDS] + } catch (ex: CancellationException) { + // that's what we are waiting for. + } catch (ex: Exception) { + ExceptionPrinter.printHistory(ex, logger) + } + } + + logger.debug("finish heartbeat") + if (heartbeat != null && !heartbeat!!.isDone) { + heartbeat!!.cancel(true) + try { + task!![5, TimeUnit.SECONDS] + } catch (ex: CancellationException) { + // that's what we are waiting for. + } catch (ex: Exception) { + ExceptionPrinter.printHistory(ex, logger) + } + } + + if (isConnected) { + // write final heartbeat if connection is established. + writeApi!!.writePoint( + bucketName!!, org!!, Point.measurement(InfluxDbProcessor.HEARTBEAT_MEASUREMENT) + .addField(InfluxDbProcessor.HEARTBEAT_FIELD, InfluxDbProcessor.HEARTBEAT_ONLINE_VALUE) + .time(System.currentTimeMillis() - 1, WritePrecision.MS) + ) + writeApi!!.writePoint( + bucketName!!, org!!, Point.measurement(InfluxDbProcessor.HEARTBEAT_MEASUREMENT) + .addField(InfluxDbProcessor.HEARTBEAT_FIELD, InfluxDbProcessor.HEARTBEAT_OFFLINE_VALUE) + .time(System.currentTimeMillis(), WritePrecision.MS) + ) + writeApi!!.flush() + } + + // deregister + customUnitPool.removeServiceStateObserver(unitStateObserver) + customUnitPool.deactivate() + disconnectDatabase() + + super.stop(activationState) + } + + @Throws(InitializationException::class, InterruptedException::class) + fun startObservation() { + try { + // setup pool + customUnitPool.addServiceStateObserver(unitStateObserver) + customUnitPool.activate() + + for (unitConfig in Registries.getUnitRegistry(true).unitConfigs) { + val unit: UnitRemote<*> + try { + unit = Units.getFutureUnit(unitConfig, true) + .get(InfluxDbProcessor.MAX_INITIAL_STORAGE_TIMEOUT, TimeUnit.MILLISECONDS) + } catch (ex: ExecutionException) { + ExceptionPrinter.printHistory( + "Could not reach Unit " + getBestMatch(unitConfig.label) + "! Skip initial service state synchronisation because unit will be synchronized anyway when it connection is established.", + ex, + logger, + LogLevel.DEBUG + ) + continue + } catch (ex: TimeoutException) { + ExceptionPrinter.printHistory( + "Could not reach Unit " + getBestMatch(unitConfig.label) + "! Skip initial service state synchronisation because unit will be synchronized anyway when it connection is established.", + ex, + logger, + LogLevel.DEBUG + ) + continue + } + try { + for (serviceDescription in unit.unitTemplate.serviceDescriptionList) { + if (serviceDescription.pattern != ServicePattern.PROVIDER) { + continue + } + storeServiceState(unit, serviceDescription.serviceType, true) + } + } catch (ex: CouldNotPerformException) { + ExceptionPrinter.printHistory("Could not store service state $unit", ex, logger) + } + } + } catch (ex: CouldNotPerformException) { + throw InitializationException(this, ex) + } + } + + @Throws(CouldNotPerformException::class) + private fun storeServiceState( + unit: org.openbase.bco.dal.lib.layer.unit.Unit<*>, + serviceType: ServiceTemplateType.ServiceTemplate.ServiceType, + initialSync: Boolean, + ) { + val currentServiceState: Message + var lastServiceState: Message? = null + try { + currentServiceState = Services.invokeProviderServiceMethod( + serviceType, + ServiceTempus.CURRENT, + unit.data + ) + } catch (ex: NotAvailableException) { + // if the current state is not available, we just try to at least store the last known service state if available. + + try { + lastServiceState = Services.invokeProviderServiceMethod( + serviceType, + ServiceTempus.LAST, + unit.data + ) + storeServiceState(unit, serviceType, lastServiceState) + } catch (exx: CouldNotPerformException) { + // we don't care if the last service state is not available + // which can be the case for an initial sync + // or any states which got only one state update since system startup. + } + return + } + + // store t - 1 entry + try { + lastServiceState = Services.invokeProviderServiceMethod( + serviceType, + ServiceTempus.LAST, + unit.data + ) + val serviceStateTimestamp = TimestampProcessor.getTimestamp(currentServiceState, TimeUnit.MILLISECONDS) - 1L + lastServiceState = + TimestampProcessor.updateTimestamp(serviceStateTimestamp, lastServiceState, TimeUnit.MILLISECONDS) + storeServiceState(unit, serviceType, lastServiceState) + } catch (ex: CouldNotPerformException) { + // we don't care if the last service state is not available + // which can be the case for an initial sync + // or any states which got only one state update since system startup. + } + + try { + storeServiceState(unit, serviceType, currentServiceState) + } catch (ex: CouldNotPerformException) { + // filter log if initial timestamps are missing + + if (initialSync) { + return + } + + ExceptionPrinter.printHistory( + "Could not store service state change into db! " + + "UnitType[" + unit.unitType + "] " + + "ServiceType[" + serviceType + "] " + + "CurrentServiceState[" + currentServiceState + "] " + + "LastServiceState[" + lastServiceState + "]", + ex, logger, LogLevel.DEBUG + ) + } + } + + @Throws(InvalidStateException::class) + private fun storeServiceState( + unit: org.openbase.bco.dal.lib.layer.unit.Unit<*>, + serviceType: ServiceTemplateType.ServiceTemplate.ServiceType, + serviceState: Message?, + ) { + val timestamp = TimestampProcessor.getTimestamp(serviceState, TimeUnit.MILLISECONDS) + try { + var initiator = try { + Services.getResponsibleAction(serviceState).getActionInitiator() + .getInitiatorType().name.lowercase( + Locale.getDefault() + ) + } catch (ex: NotAvailableException) { + // in this case we use the system as initiator because responsible actions are not available for pure provider services and those are always system generated. + "system" + } + val stateValuesMap = resolveStateValueToMap(serviceState) + val point = Point.measurement(serviceType.name.lowercase(Locale.getDefault())) + .addTag("alias", unit.config.getAlias(0)) + .addTag("initiator", initiator) + .addTag("unit_id", unit.id) + .addTag("unit_type", unit.unitType.name.lowercase(Locale.getDefault())) + .addTag("location_id", unit.parentLocationConfig.id) + .addTag("location_alias", unit.parentLocationConfig.getAlias(0)) + .time(timestamp, WritePrecision.MS) + + var values = 0 + for ((key, value) in stateValuesMap) { + // detect numbers with regex + if (value.matches("-?\\d+(\\.\\d+)?".toRegex())) { + values++ + point.addField(key, value.toDouble()) + } else { + point.addTag(key, value) + } + } + + val entryList = unit.config.label.entryList + for (entry in entryList) { + // skip entries that offer a language key but do not provide any label. + if (entry.valueList.isEmpty()) { + continue + } + point.addTag("label_" + entry.key, entry.getValue(0)) + } + + if (values > 0) { + writeApi!!.writePoint(bucketName!!, org!!, point) + } + } catch (ex: CouldNotPerformException) { + ExceptionPrinter.printHistory( + "Could not store service state " + serviceType.name + " of " + unit, + ex, + logger + ) + } + } + + + @Throws(CouldNotPerformException::class) + fun resolveStateValueToMap(serviceState: Message?): Map { + val stateValues: MutableMap = HashMap() + for (fieldDescriptor in serviceState!!.descriptorForType.fields) { + val stateName = fieldDescriptor.name + var stateType = fieldDescriptor.type.toString().lowercase(Locale.getDefault()) + + // filter invalid states + if (stateName == null) { + logger.warn("Could not detect datatype of $stateType") + } + + when (stateName) { + "aggregated_value_coverage", "last_value_occurrence", "timestamp", "responsible_action", "type", "rgb_color", "frame_id" -> continue + } + // filter data units + if (stateName!!.endsWith("data_unit")) { + continue + } + + var stateValue = serviceState.getField(fieldDescriptor).toString() + + try { + if (fieldDescriptor.type == Descriptors.FieldDescriptor.Type.MESSAGE) { + if (fieldDescriptor.isRepeated) { + val types: MutableList = ArrayList() + + for (i in 0 until serviceState.getRepeatedFieldCount(fieldDescriptor)) { + val repeatedFieldEntry = serviceState.getRepeatedField(fieldDescriptor, i) + if (repeatedFieldEntry is Message) { + types.add( + "[" + Services.resolveStateValue( + repeatedFieldEntry + ).toString() + "]" + ) + } + types.add(repeatedFieldEntry.toString()) + } + stateType = types.toString().lowercase(Locale.getDefault()) + } else { + stateValue = Services.resolveStateValue( + serviceState.getField(fieldDescriptor) as Message + ).toString() + } + } + } catch (ex: InvalidStateException) { + logger.warn("Could not process value of " + fieldDescriptor.name) + continue + } + + when (stateValue) { + "", "NaN" -> continue + else -> {} + } + if (fieldDescriptor.javaType == Descriptors.FieldDescriptor.JavaType.ENUM) { + val finalStateValue = stateValue + stateValue = fieldDescriptor.enumType.values.stream() + .filter { `val`: Descriptors.EnumValueDescriptor -> `val`.name == finalStateValue } + .findFirst().get().number.toString() + } + + stateValues[fieldDescriptor.name] = stateValue.lowercase(Locale.getDefault()) + } + return stateValues + } + + private val isConnected: Boolean + /** + * Method checks if the connection is established. + * + * @return true if the connection to influx db is established, otherwise false. + */ + get() { + try { + verifyConnection() + } catch (ex: VerificationFailedException) { + return false + } + return true + } + + /** + * Method verifies the connection state. + * + * @throws VerificationFailedException is thrown if the connection is not established. + */ + @Throws(VerificationFailedException::class) + private fun verifyConnection() { + if (influxDBClient == null) { + throw VerificationFailedException("Influx db connection has never been initiated.") + } + + if (influxDBClient!!.health().status.value !== "pass") { + throw VerificationFailedException("Could not connect to database server at $databaseUrl!") + } + + // initiate WriteApi + val writeoptions = WriteOptions.builder().batchSize(batchLimit!!).flushInterval(batchTime!!).build() + writeApi = influxDBClient!!.getWriteApi(writeoptions) + writeApi!!.listenEvents(WriteSuccessEvent::class.java) { event: WriteSuccessEvent? -> + logger.debug("Successfully wrote data into db") + } + writeApi!!.listenEvents(WriteErrorEvent::class.java) { event: WriteErrorEvent -> + val exception = event.throwable + logger.warn(exception.message) + } + logger.debug("Connected to Influxdb at $databaseUrl") + } + + private fun connectToDatabase() { + try { + influxDBClient?.close() + } catch (ex: Exception) { + ExceptionPrinter.printHistory("Could not shutdown database connection!", ex, logger) + } + logger.debug(" Try to connect to influxDB at $databaseUrl") + + + token?.let { + influxDBClient = InfluxDBClientFactory.create( + databaseUrl + "?readTimeout=" + InfluxDbProcessor.READ_TIMEOUT + "&connectTimeout=" + InfluxDbProcessor.CONNECT_TIMOUT + "&writeTimeout=" + InfluxDbProcessor.WRITE_TIMEOUT + "&logLevel=BASIC", + it + ) + } + } + + private fun disconnectDatabase() { + try { + if (influxDBClient != null) { + if (writeApi != null) { + writeApi!!.flush() + } + influxDBClient!!.close() + writeApi = null + } + } catch (ex: Exception) { + ExceptionPrinter.printHistory("Could not shutdown database connection!", ex, logger) + } + } + + @get:Throws(NotAvailableException::class) + private val databaseBucket: Unit + get() { + logger.debug("Get bucket $bucketName") + bucket = influxDBClient!!.bucketsApi.findBucketByName(bucketName!!) + if (bucket == null) { + throw NotAvailableException("bucket", bucketName) + } + } +} diff --git a/module/app/preset/src/main/java/org/openbase/bco/app/preset/NightLightApp.java b/module/app/preset/src/main/java/org/openbase/bco/app/preset/NightLightApp.java deleted file mode 100644 index cbd53b465a..0000000000 --- a/module/app/preset/src/main/java/org/openbase/bco/app/preset/NightLightApp.java +++ /dev/null @@ -1,360 +0,0 @@ -package org.openbase.bco.app.preset; - -/* - * #%L - * BCO App Preset - * %% - * Copyright (C) 2018 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController; -import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.bco.dal.remote.action.RemoteAction; -import org.openbase.bco.dal.remote.layer.unit.Units; -import org.openbase.bco.dal.remote.layer.unit.location.LocationRemote; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.jul.exception.*; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.iface.Activatable; -import org.openbase.jul.pattern.Observer; -import org.openbase.jul.pattern.provider.DataProvider; -import org.openbase.jul.schedule.CloseableWriteLockWrapper; -import org.openbase.jul.schedule.SyncObject; -import org.openbase.jul.schedule.Timeout; -import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; -import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType; -import org.openbase.type.domotic.state.ActivationStateType.ActivationState; -import org.openbase.type.domotic.state.PowerStateType.PowerState.State; -import org.openbase.type.domotic.state.PresenceStateType.PresenceState; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; -import org.openbase.type.domotic.unit.location.LocationConfigType.LocationConfig.LocationType; -import org.openbase.type.domotic.unit.location.LocationDataType.LocationData; -import org.openbase.type.vision.HSBColorType.HSBColor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; -import java.util.concurrent.TimeUnit; - -/** - * UnitConfig - */ -public class NightLightApp extends AbstractAppController { - - public static final HSBColor COLOR_ORANGE = HSBColor.newBuilder().setHue(15d).setSaturation(1.0d).setBrightness(0.10d).build(); - private static final Logger LOGGER = LoggerFactory.getLogger(NightLightApp.class); - private static final String META_CONFIG_KEY_EXCLUDE_LOCATION = "EXCLUDE_LOCATION"; - - private SyncObject locationMapLock = new SyncObject("LocationMapLock"); - - private Map presentsActionLocationMap, absenceActionLocationMap; - private Map locationMap; - - public NightLightApp() throws InstantiationException { - this.locationMap = new HashMap<>(); - this.presentsActionLocationMap = new HashMap<>(); - this.absenceActionLocationMap = new HashMap<>(); - } - - /** - * @param location the location to process - * @param eventSource the source which has triggered the update: location / childlocation / timeout / init (null) - * @param timeout the timeout of the location - */ - private void update(final LocationRemote location, final Object eventSource, final Timeout timeout) { - try { - - // skip update when not active - if (getActivationState().getValue() != ActivationState.State.ACTIVE) { - - if (!executing) { - logger.warn("app inactive but still executing! Force stopp..."); - try { - stop(getActivationState()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } - } - logger.error("Update triggered even when not active!"); - StackTracePrinter.printStackTrace(logger); - return; - } - - // init present state with main location. - PresenceState.State presentState = location.getPresenceState().getValue(); - for (final LocationRemote neighbor : location.getNeighborLocationList(true)) { - - // break if any present person is detected. - if (presentState == PresenceState.State.PRESENT) { - break; - } - - // if not unknown apply state of neighbor - if (neighbor.getPresenceState().getValue() != PresenceState.State.UNKNOWN) { - presentState = neighbor.getPresenceState().getValue(); - } - } - - final RemoteAction absenceAction = absenceActionLocationMap.get(location); - final RemoteAction presentsAction = presentsActionLocationMap.get(location); - - switch (presentState) { - case PRESENT: - - if (eventSource == location) { - timeout.restart(10, TimeUnit.MINUTES); - } else { - timeout.restart(2, TimeUnit.MINUTES); - } - - // if ongoing action skip the update. - if (presentsAction != null && !presentsAction.isDone()) { - return; - } - - // System.out.println("Nightmode: switch location " + location.getLabel() + " to orange because of present state]."); - final Set availableServiceTypes = location.getAvailableServiceTypes(); - if (availableServiceTypes.contains(ServiceType.COLOR_STATE_SERVICE)) { - presentsActionLocationMap.put(location, observe(location.setColor(COLOR_ORANGE, getDefaultActionParameter()))); - } else if (availableServiceTypes.contains(ServiceType.BRIGHTNESS_STATE_SERVICE)) { - presentsActionLocationMap.put(location, observe(location.setBrightness(COLOR_ORANGE.getBrightness(), getDefaultActionParameter()))); - } else { - // location not supported for nightlight - } - - break; - case ABSENT: - - // if ongoing action skip the update. - if (absenceAction != null && !absenceAction.isDone()) { - return; - } - - if (timeout.isExpired() || !timeout.isActive()) { - // System.out.println("Nightmode: switch off " + location.getLabel() + " because of absent state."); - absenceActionLocationMap.put(location, observe(location.setPowerState(State.OFF, UnitType.LIGHT, getDefaultActionParameter()))); - - // cancel presents actions - if (presentsAction != null) { - presentsAction.cancel(); - presentsActionLocationMap.remove(location); - } - } - - break; - } - } catch (ShutdownInProgressException ex) { - // skip update when shutdown was initiated. - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not control light in " + location.getLabel("?") + " by night light!", ex, LOGGER); - } - } - - @Override - public UnitConfig applyConfigUpdate(UnitConfig config) throws CouldNotPerformException, InterruptedException { - try (final CloseableWriteLockWrapper ignored = getManageWriteLockInterruptible(this)) { - config = super.applyConfigUpdate(config); - updateLocationMap(); - return config; - } - } - - private void updateLocationMap() throws CouldNotPerformException { - try { - synchronized (locationMapLock) { - - // deregister all tile remotes - locationMap.forEach((remote, observer) -> { - observer.deactivate(); - }); - - // clear all tile remotes - locationMap.clear(); - - final Collection excludedLocations = new ArrayList<>(); - - // check location exclusion - try { - excludedLocations.addAll(generateVariablePool().getValues(META_CONFIG_KEY_EXCLUDE_LOCATION).values()); - } catch (final NotAvailableException ex) { - ExceptionPrinter.printHistory("Could not load variable pool!", ex, LOGGER); - // no locations excluded. - } - - // load parent location - final UnitConfig parentLocationConfig = Registries.getUnitRegistry().getUnitConfigById(getConfig().getPlacementConfig().getLocationId()); - - // load tile remotes - remoteLocationLoop: - for (String childLocationId : parentLocationConfig.getLocationConfig().getChildIdList()) { - - final UnitConfig locationUnitConfig = Registries.getUnitRegistry().getUnitConfigById(childLocationId); - - // let only tiles pass - if (locationUnitConfig.getLocationConfig().getLocationType() != LocationType.TILE) { - continue; - } - - // check if location was excluded by id - if (excludedLocations.contains(locationUnitConfig.getId())) { - // System.out.println("exclude locations: " + locationUnitConfig.getLabel()); - continue remoteLocationLoop; - } - - // check if location was excluded by alias - for (String alias : locationUnitConfig.getAliasList()) { - if (excludedLocations.contains(alias)) { - // System.out.println("exclude locations: " + locationUnitConfig.getLabel()); - continue remoteLocationLoop; - } - } - - final LocationRemote remote = Units.getUnit(locationUnitConfig, false, Units.LOCATION); - - // skip locations without colorable lights. - if (!remote.isServiceAvailable(ServiceType.COLOR_STATE_SERVICE)) { - continue remoteLocationLoop; - } - - locationMap.put(remote, new TimedObserver(remote)); - } - - if (getActivationState().getValue() == ActivationState.State.ACTIVE) { - locationMap.forEach((remote, observer) -> { - observer.activate(); - }); - } - } - } catch (final InterruptedException ex) { - Thread.currentThread().interrupt(); - } catch (final CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not update location map", ex); - } - } - - @Override - public void shutdown() { - super.shutdown(); - synchronized (locationMapLock) { - locationMap.clear(); - } - } - - boolean executing = false; - - @Override - protected ActionDescription execute(final ActivationState activationState) throws CouldNotPerformException, InterruptedException { - executing = true; - synchronized (locationMapLock) { - locationMap.forEach((remote, observer) -> { - observer.activate(); - }); - } - return activationState.getResponsibleAction(); - } - - @Override - protected void stop(final ActivationState activationState) throws InterruptedException, CouldNotPerformException { - executing = false; - synchronized (locationMapLock) { - - // remove observer - locationMap.forEach((remote, observer) -> { - observer.deactivate(); - }); - - // clear actions list - presentsActionLocationMap.clear(); - absenceActionLocationMap.clear(); - } - super.stop(activationState); - } - - class TimedObserver implements Activatable { - - final LocationRemote remote; - final Timeout timeout; - final Observer, LocationData> internalObserver; - final List neighborLocationRemoteList; - - boolean active; - - public TimedObserver(LocationRemote remote) throws InstantiationException { - try { - this.remote = remote; - this.timeout = new Timeout(1, TimeUnit.MINUTES) { - @Override - public void expired() { - // skip update when not active - if (!active) { - logger.error("Update triggered even when not active!"); - StackTracePrinter.printStackTrace(logger); - return; - } - NightLightApp.this.update(remote, this, this); - } - }; - this.neighborLocationRemoteList = remote.getNeighborLocationList(false); - this.internalObserver = (source, data) -> { - // skip update when not active - if (!active) { - logger.error("Update triggered even when not active!"); - StackTracePrinter.printStackTrace(logger); - return; - } - NightLightApp.this.update(remote, source, timeout); - }; - } catch (CouldNotPerformException ex) { - throw new InstantiationException(this, ex); - } - } - - @Override - public synchronized void activate() { - active = true; - - // register observer - remote.addDataObserver(internalObserver); - for (LocationRemote neighbor : neighborLocationRemoteList) { - neighbor.addDataObserver(internalObserver); - } - - NightLightApp.this.update(remote, null, timeout); - } - - @Override - public synchronized void deactivate() { - active = false; - timeout.cancel(); - - // deregister observer - remote.removeDataObserver(internalObserver); - for (LocationRemote neighbor : neighborLocationRemoteList) { - neighbor.removeDataObserver(internalObserver); - } - } - - @Override - public boolean isActive() { - return active; - } - } -} diff --git a/module/app/preset/src/main/java/org/openbase/bco/app/preset/NightLightApp.kt b/module/app/preset/src/main/java/org/openbase/bco/app/preset/NightLightApp.kt new file mode 100644 index 0000000000..5337c3f317 --- /dev/null +++ b/module/app/preset/src/main/java/org/openbase/bco/app/preset/NightLightApp.kt @@ -0,0 +1,347 @@ +package org.openbase.bco.app.preset + +import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController +import org.openbase.bco.dal.lib.layer.unit.Unit +import org.openbase.bco.dal.remote.action.RemoteAction +import org.openbase.bco.dal.remote.layer.unit.Units +import org.openbase.bco.dal.remote.layer.unit.location.LocationRemote +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.InstantiationException +import org.openbase.jul.exception.NotAvailableException +import org.openbase.jul.exception.ShutdownInProgressException +import org.openbase.jul.exception.StackTracePrinter.printStackTrace +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.iface.Activatable +import org.openbase.jul.pattern.Observer +import org.openbase.jul.pattern.provider.DataProvider +import org.openbase.jul.schedule.SyncObject +import org.openbase.jul.schedule.Timeout +import org.openbase.type.domotic.action.ActionDescriptionType +import org.openbase.type.domotic.service.ServiceTemplateType +import org.openbase.type.domotic.state.ActivationStateType +import org.openbase.type.domotic.state.PowerStateType +import org.openbase.type.domotic.state.PresenceStateType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.UnitTemplateType +import org.openbase.type.domotic.unit.location.LocationConfigType +import org.openbase.type.domotic.unit.location.LocationDataType +import org.openbase.type.vision.HSBColorType +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.util.concurrent.TimeUnit + +/** + * UnitConfig + */ +class NightLightApp : AbstractAppController() { + private val locationMapLock = SyncObject("LocationMapLock") + + private val presentsActionLocationMap: MutableMap?, RemoteAction> + private val absenceActionLocationMap: MutableMap?, RemoteAction> + private val locationMap: MutableMap + + /** + * @param location the location to process + * @param eventSource the source which has triggered the update: location / childlocation / timeout / init (null) + * @param timeout the timeout of the location + */ + private fun update(location: LocationRemote?, eventSource: Any?, timeout: Timeout?) { + try { + // skip update when not active + + if (activationState.value != ActivationStateType.ActivationState.State.ACTIVE) { + if (!executing) { + logger.warn("app inactive but still executing! Force stopp...") + try { + stop(activationState) + } catch (e: InterruptedException) { + Thread.currentThread().interrupt() + return + } + } + logger.error("Update triggered even when not active!") + printStackTrace(logger) + return + } + + // init present state with main location. + var presentState = location!!.presenceState.value + for (neighbor in location.getNeighborLocationList(true)) { + // break if any present person is detected. + + if (presentState == PresenceStateType.PresenceState.State.PRESENT) { + break + } + + // if not unknown apply state of neighbor + if (neighbor.presenceState.value != PresenceStateType.PresenceState.State.UNKNOWN) { + presentState = neighbor.presenceState.value + } + } + + val absenceAction = absenceActionLocationMap[location] + val presentsAction = presentsActionLocationMap[location] + + when (presentState) { + PresenceStateType.PresenceState.State.PRESENT -> { + if (eventSource === location) { + timeout!!.restart(10, TimeUnit.MINUTES) + } else { + timeout!!.restart(2, TimeUnit.MINUTES) + } + + // if ongoing action skip the update. + if (presentsAction != null && !presentsAction.isDone) { + return + } + + // System.out.println("Nightmode: switch location " + location.getLabel() + " to orange because of present state]."); + val availableServiceTypes = location.availableServiceTypes + if (availableServiceTypes.contains(ServiceTemplateType.ServiceTemplate.ServiceType.COLOR_STATE_SERVICE)) { + presentsActionLocationMap[location] = + observe(location.setColor(COLOR_ORANGE, defaultActionParameter)) + } else if (availableServiceTypes.contains(ServiceTemplateType.ServiceTemplate.ServiceType.BRIGHTNESS_STATE_SERVICE)) { + presentsActionLocationMap[location] = + observe(location.setBrightness(COLOR_ORANGE.brightness, defaultActionParameter)) + } else { + // location not supported for nightlight + } + } + + PresenceStateType.PresenceState.State.ABSENT -> { + // if ongoing action skip the update. + if (absenceAction != null && !absenceAction.isDone) { + return + } + + if (timeout!!.isExpired || !timeout.isActive) { + // System.out.println("Nightmode: switch off " + location.getLabel() + " because of absent state."); + absenceActionLocationMap[location] = observe( + location.setPowerState( + PowerStateType.PowerState.State.OFF, + UnitTemplateType.UnitTemplate.UnitType.LIGHT, + defaultActionParameter + ) + ) + + // cancel presents actions + if (presentsAction != null) { + presentsAction.cancel() + presentsActionLocationMap.remove(location) + } + } + } + + PresenceStateType.PresenceState.State.UNKNOWN -> TODO() + } + } catch (ex: ShutdownInProgressException) { + // skip update when shutdown was initiated. + } catch (ex: CouldNotPerformException) { + ExceptionPrinter.printHistory( + "Could not control light in " + location!!.getLabel("?") + " by night light!", + ex, + LOGGER + ) + } + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun applyConfigUpdate(config: UnitConfigType.UnitConfig): UnitConfigType.UnitConfig { + var config: UnitConfigType.UnitConfig? = config + getManageWriteLockInterruptible(this).use { ignored -> + config = super.applyConfigUpdate(config!!) + updateLocationMap() + return config!! + } + } + + @Throws(CouldNotPerformException::class) + private fun updateLocationMap() { + try { + synchronized(locationMapLock) { + // deregister all tile remotes + locationMap.forEach { (remote: LocationRemote?, observer: TimedObserver) -> + observer.deactivate() + } + + // clear all tile remotes + locationMap.clear() + + val excludedLocations: MutableCollection = ArrayList() + + // check location exclusion + try { + excludedLocations.addAll(generateVariablePool().getValues(META_CONFIG_KEY_EXCLUDE_LOCATION).values) + } catch (ex: NotAvailableException) { + ExceptionPrinter.printHistory("Could not load variable pool!", ex, LOGGER) + // no locations excluded. + } + + // load parent location + val parentLocationConfig = + Registries.getUnitRegistry().getUnitConfigById(config.placementConfig.locationId) + + // load tile remotes + remoteLocationLoop@ for (childLocationId in parentLocationConfig.locationConfig.childIdList) { + val locationUnitConfig = Registries.getUnitRegistry().getUnitConfigById(childLocationId) + + // let only tiles pass + if (locationUnitConfig.locationConfig.locationType != LocationConfigType.LocationConfig.LocationType.TILE) { + continue + } + + // check if location was excluded by id + if (excludedLocations.contains(locationUnitConfig.id)) { + // System.out.println("exclude locations: " + locationUnitConfig.getLabel()); + continue@remoteLocationLoop + } + + // check if location was excluded by alias + for (alias in locationUnitConfig.aliasList) { + if (excludedLocations.contains(alias)) { + // System.out.println("exclude locations: " + locationUnitConfig.getLabel()); + continue@remoteLocationLoop + } + } + + val remote = Units.getUnit(locationUnitConfig, false, Units.LOCATION) + + // skip locations without colorable lights. + if (!remote.isServiceAvailable(ServiceTemplateType.ServiceTemplate.ServiceType.COLOR_STATE_SERVICE)) { + continue@remoteLocationLoop + } + + locationMap[remote] = TimedObserver(remote) + } + if (activationState.value == ActivationStateType.ActivationState.State.ACTIVE) { + locationMap.forEach { (remote: LocationRemote?, observer: TimedObserver) -> + observer.activate() + } + } + } + } catch (ex: InterruptedException) { + Thread.currentThread().interrupt() + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException("Could not update location map", ex) + } + } + + override fun shutdown() { + super.shutdown() + synchronized(locationMapLock) { + locationMap.clear() + } + } + + var executing: Boolean = false + + init { + this.locationMap = HashMap() + this.presentsActionLocationMap = HashMap() + this.absenceActionLocationMap = HashMap() + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun execute(activationState: ActivationStateType.ActivationState): ActionDescriptionType.ActionDescription? { + executing = true + synchronized(locationMapLock) { + locationMap.forEach { (remote: LocationRemote?, observer: TimedObserver) -> + observer.activate() + } + } + return activationState.responsibleAction + } + + @Throws(InterruptedException::class, CouldNotPerformException::class) + override fun stop(activationState: ActivationStateType.ActivationState) { + executing = false + synchronized(locationMapLock) { + // remove observer + locationMap.forEach { (remote: LocationRemote?, observer: TimedObserver) -> + observer.deactivate() + } + + // clear actions list + presentsActionLocationMap.clear() + absenceActionLocationMap.clear() + } + super.stop(activationState) + } + + internal inner class TimedObserver(remote: LocationRemote) : Activatable { + var remote: LocationRemote? = null + var timeout: Timeout? = null + var internalObserver: Observer, LocationDataType.LocationData>? = + null + var neighborLocationRemoteList: List? = null + + var active: Boolean = false + + init { + try { + this.remote = remote + this.timeout = object : Timeout(1, TimeUnit.MINUTES) { + override fun expired() { + // skip update when not active + if (!active) { + logger.error("Update triggered even when not active!") + printStackTrace(logger) + return + } + this@NightLightApp.update(remote, this, this) + } + } + this.neighborLocationRemoteList = remote.getNeighborLocationList(false) + this.internalObserver = + Observer { source: DataProvider?, data: LocationDataType.LocationData? -> + // skip update when not active + if (!active) { + logger.error("Update triggered even when not active!") + printStackTrace(logger) + return@Observer + } + this@NightLightApp.update(remote, source, timeout) + } + } catch (ex: CouldNotPerformException) { + throw InstantiationException(this, ex) + } + } + + @Synchronized + override fun activate() { + active = true + + // register observer + remote!!.addDataObserver(internalObserver) + for (neighbor in neighborLocationRemoteList!!) { + neighbor.addDataObserver(internalObserver) + } + + this@NightLightApp.update(remote, null, timeout) + } + + @Synchronized + override fun deactivate() { + active = false + timeout!!.cancel() + + // deregister observer + remote!!.removeDataObserver(internalObserver) + for (neighbor in neighborLocationRemoteList!!) { + neighbor.removeDataObserver(internalObserver) + } + } + + override fun isActive(): Boolean { + return active + } + } + + companion object { + val COLOR_ORANGE: HSBColorType.HSBColor = + HSBColorType.HSBColor.newBuilder().setHue(15.0).setSaturation(1.0).setBrightness(0.10).build() + private val LOGGER: Logger = LoggerFactory.getLogger(NightLightApp::class.java) + private const val META_CONFIG_KEY_EXCLUDE_LOCATION = "EXCLUDE_LOCATION" + } +} diff --git a/module/app/preset/src/main/java/org/openbase/bco/app/preset/PartyLightTileFollowerApp.java b/module/app/preset/src/main/java/org/openbase/bco/app/preset/PartyLightTileFollowerApp.java deleted file mode 100644 index 04fe1f3817..0000000000 --- a/module/app/preset/src/main/java/org/openbase/bco/app/preset/PartyLightTileFollowerApp.java +++ /dev/null @@ -1,185 +0,0 @@ -package org.openbase.bco.app.preset; - -/* - * #%L - * BCO App Preset - * %% - * Copyright (C) 2018 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.bco.dal.lib.layer.unit.Unit; -import org.openbase.bco.dal.remote.action.RemoteAction; -import org.openbase.bco.dal.remote.layer.unit.Units; -import org.openbase.bco.dal.remote.layer.unit.location.LocationRemote; -import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.ExceptionProcessor; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.schedule.SyncObject; -import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; -import org.openbase.type.domotic.state.ActivationStateType.ActivationState; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; -import org.openbase.type.domotic.unit.location.LocationConfigType; -import org.openbase.type.vision.HSBColorType.HSBColor; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.*; - -/** - * UnitConfig - */ -public class PartyLightTileFollowerApp extends AbstractAppController { - - private Map actionLocationMap; - private Map locationRemoteMap; - - private SyncObject taskLock = new SyncObject("TaskLock"); - - public PartyLightTileFollowerApp() throws InstantiationException, InterruptedException { - try { - Registries.waitForData(); - this.actionLocationMap = new HashMap<>(); - this.locationRemoteMap = new HashMap<>(); - } catch (CouldNotPerformException ex) { - throw new InstantiationException(this, ex); - } - } - - @Override - public void shutdown() { - locationRemoteMap.clear(); - super.shutdown(); - } - - private double brightness = 0.50d; - private HSBColor[] colors = { - HSBColor.newBuilder().setHue(0d).setSaturation(1.0d).setBrightness(brightness).build(), - HSBColor.newBuilder().setHue(290d).setSaturation(1.0d).setBrightness(brightness).build(), - HSBColor.newBuilder().setHue(30d).setSaturation(1.0d).setBrightness(brightness).build(),}; - - @Override - protected ActionDescription execute(final ActivationState activationState) throws CouldNotPerformException, InterruptedException { - logger.debug("Execute PartyLightTileFollowerApp[" + getLabel() + "]"); - - // init tile remotes - locationRemoteMap.clear(); - for (final UnitConfig locationUnitConfig : Registries.getUnitRegistry(true).getUnitConfigsByUnitType(UnitType.LOCATION)) { - if (!locationUnitConfig.getLocationConfig().getLocationType().equals(LocationConfigType.LocationConfig.LocationType.TILE)) { - continue; - } - locationRemoteMap.put(locationUnitConfig.getId(), Units.getUnit(locationUnitConfig, false, Units.LOCATION)); - } - - new TileFollower().call(); - return activationState.getResponsibleAction(); - } - - @Override - protected void stop(final ActivationState activationState) throws InterruptedException, CouldNotPerformException { - final ArrayList> cancelTaskList = new ArrayList<>(); - for (Entry unitActionEntry : actionLocationMap.entrySet()) { - cancelTaskList.add(unitActionEntry.getValue().cancel()); - } - - for (Future cancelTask : cancelTaskList) { - try { - cancelTask.get(10, TimeUnit.SECONDS); - } catch (ExecutionException | TimeoutException ex) { - if(!ExceptionProcessor.isCausedBySystemShutdown(ex)) { - ExceptionPrinter.printHistory("Could not cancel action!", ex, logger); - } - } - } - super.stop(activationState); - } - - public class TileFollower implements Callable { - - private final List processedLocations = new ArrayList<>(); - - @Override - public Void call() throws CouldNotPerformException, InterruptedException { - if (locationRemoteMap.isEmpty()) { - throw new CouldNotPerformException("No Locations found!"); - } - - LocationRemote locationRemote; - - int colorIndex = 0; - while (!Thread.currentThread().isInterrupted()) { - Thread.yield(); - try { - // apply updates for next iteration - colorIndex = ++colorIndex % colors.length; - processedLocations.clear(); - - // select initial room - locationRemote = locationRemoteMap.get(getConfig().getPlacementConfig().getLocationId()); - - processRoom(locationRemote, colors[colorIndex]); - - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory(new CouldNotPerformException("Skip animation run!", ExceptionProcessor.interruptOnShutdown(ex)), logger); - } - } - return null; - } - - private void processRoom(final LocationRemote locationRemote, final HSBColor color) throws CouldNotPerformException, InterruptedException { - logger.debug("Set " + locationRemote + " to " + color + "..."); - try { - - // skip if no colorable light is present - if (!Registries.getUnitRegistry().getUnitConfigsByLocationIdAndUnitType(locationRemote.getId(), UnitType.COLORABLE_LIGHT).isEmpty()) { - if (locationRemote.isConnected() && locationRemote.isDataAvailable()) { - final RemoteAction remoteAction = observe(locationRemote.setColor(color, getDefaultActionParameter())); - actionLocationMap.put(locationRemote, remoteAction); - remoteAction.waitForRegistration(); - } - Thread.sleep(1000); - } - - // mark as processed - processedLocations.add(locationRemote.getId()); - - // process neighbors - LocationRemote neighborRemote; - for (UnitConfig neighborConfig : Registries.getUnitRegistry().getNeighborLocationsByLocationId(locationRemote.getId())) { - // skip if already processed - if (processedLocations.contains(neighborConfig.getId())) { - continue; - } - - neighborRemote = locationRemoteMap.get(neighborConfig.getId()); - - // process remote - processRoom(neighborRemote, color); - } - } catch (CouldNotPerformException ex) { - throw new CouldNotPerformException("Could not process room of " + locationRemote, ex); - } - } - } -} diff --git a/module/app/preset/src/main/java/org/openbase/bco/app/preset/PartyLightTileFollowerApp.kt b/module/app/preset/src/main/java/org/openbase/bco/app/preset/PartyLightTileFollowerApp.kt new file mode 100644 index 0000000000..e89aa9a863 --- /dev/null +++ b/module/app/preset/src/main/java/org/openbase/bco/app/preset/PartyLightTileFollowerApp.kt @@ -0,0 +1,171 @@ +package org.openbase.bco.app.preset + +import org.openbase.bco.dal.control.layer.unit.app.AbstractAppController +import org.openbase.bco.dal.lib.layer.unit.Unit +import org.openbase.bco.dal.remote.action.RemoteAction +import org.openbase.bco.dal.remote.layer.unit.Units +import org.openbase.bco.dal.remote.layer.unit.location.LocationRemote +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.ExceptionProcessor.interruptOnShutdown +import org.openbase.jul.exception.ExceptionProcessor.isCausedBySystemShutdown +import org.openbase.jul.exception.InstantiationException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.schedule.SyncObject +import org.openbase.type.domotic.action.ActionDescriptionType +import org.openbase.type.domotic.state.ActivationStateType +import org.openbase.type.domotic.unit.UnitTemplateType +import org.openbase.type.domotic.unit.location.LocationConfigType +import org.openbase.type.vision.HSBColorType +import java.util.concurrent.* + +/** + * UnitConfig + */ +class PartyLightTileFollowerApp : AbstractAppController() { + private var actionLocationMap: MutableMap?, RemoteAction>? = null + private var locationRemoteMap: MutableMap? = null + + private val taskLock = SyncObject("TaskLock") + + override fun shutdown() { + locationRemoteMap!!.clear() + super.shutdown() + } + + private val brightness = 0.50 + private val colors = arrayOf( + HSBColorType.HSBColor.newBuilder().setHue(0.0).setSaturation(1.0).setBrightness(brightness).build(), + HSBColorType.HSBColor.newBuilder().setHue(290.0).setSaturation(1.0).setBrightness(brightness).build(), + HSBColorType.HSBColor.newBuilder().setHue(30.0).setSaturation(1.0).setBrightness(brightness).build(), + ) + + init { + try { + Registries.waitForData() + this.actionLocationMap = HashMap() + this.locationRemoteMap = HashMap() + } catch (ex: CouldNotPerformException) { + throw InstantiationException(this, ex) + } + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun execute(activationState: ActivationStateType.ActivationState): ActionDescriptionType.ActionDescription? { + logger.debug("Execute PartyLightTileFollowerApp[$label]") + + // init tile remotes + locationRemoteMap!!.clear() + for (locationUnitConfig in Registries.getUnitRegistry(true) + .getUnitConfigsByUnitType(UnitTemplateType.UnitTemplate.UnitType.LOCATION)) { + if (locationUnitConfig.locationConfig.locationType != LocationConfigType.LocationConfig.LocationType.TILE) { + continue + } + locationRemoteMap!![locationUnitConfig.id] = Units.getUnit(locationUnitConfig, false, Units.LOCATION) + } + + TileFollower().call() + return activationState.responsibleAction + } + + @Throws(InterruptedException::class, CouldNotPerformException::class) + override fun stop(activationState: ActivationStateType.ActivationState) { + val cancelTaskList = ArrayList>() + for ((_, value) in actionLocationMap!!) { + cancelTaskList.add(value.cancel()) + } + + for (cancelTask in cancelTaskList) { + try { + cancelTask[10, TimeUnit.SECONDS] + } catch (ex: ExecutionException) { + if (!isCausedBySystemShutdown(ex)) { + ExceptionPrinter.printHistory("Could not cancel action!", ex, logger) + } + } catch (ex: TimeoutException) { + if (!isCausedBySystemShutdown(ex)) { + ExceptionPrinter.printHistory("Could not cancel action!", ex, logger) + } + } + } + super.stop(activationState) + } + + inner class TileFollower : Callable { + private val processedLocations: MutableList = ArrayList() + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun call(): Void? { + if (locationRemoteMap!!.isEmpty()) { + throw CouldNotPerformException("No Locations found!") + } + + var locationRemote: LocationRemote? + + var colorIndex = 0 + while (!Thread.currentThread().isInterrupted) { + Thread.yield() + try { + // apply updates for next iteration + colorIndex = ++colorIndex % colors.size + processedLocations.clear() + + // select initial room + locationRemote = locationRemoteMap!![config.placementConfig.locationId] + + processRoom(locationRemote, colors[colorIndex]) + } catch (ex: CouldNotPerformException) { + ExceptionPrinter.printHistory( + CouldNotPerformException( + "Skip animation run!", + interruptOnShutdown(ex) + ), logger + ) + } + } + return null + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + private fun processRoom(locationRemote: LocationRemote?, color: HSBColorType.HSBColor) { + logger.debug("Set $locationRemote to $color...") + try { + // skip if no colorable light is present + + if (Registries.getUnitRegistry().getUnitConfigsByLocationIdAndUnitType( + locationRemote!!.id, + UnitTemplateType.UnitTemplate.UnitType.COLORABLE_LIGHT + ).isNotEmpty() + ) { + if (locationRemote.isConnected && locationRemote.isDataAvailable) { + val remoteAction = observe(locationRemote.setColor(color, defaultActionParameter)) + actionLocationMap!![locationRemote] = remoteAction + remoteAction.waitForRegistration() + } + Thread.sleep(1000) + } + + // mark as processed + processedLocations.add(locationRemote.id) + + // process neighbors + var neighborRemote: LocationRemote? + for (neighborConfig in Registries.getUnitRegistry().getNeighborLocationsByLocationId( + locationRemote.id + )) { + // skip if already processed + if (processedLocations.contains(neighborConfig.id)) { + continue + } + + neighborRemote = locationRemoteMap!![neighborConfig.id] + + // process remote + processRoom(neighborRemote, color) + } + } catch (ex: CouldNotPerformException) { + throw CouldNotPerformException("Could not process room of $locationRemote", ex) + } + } + } +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.java deleted file mode 100644 index e79e1400e7..0000000000 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.java +++ /dev/null @@ -1,162 +0,0 @@ -package org.openbase.bco.dal.control.layer.unit; - -/*- - * #%L - * BCO DAL Control - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import com.google.protobuf.AbstractMessage; -import org.openbase.bco.dal.lib.action.ActionDescriptionProcessor; -import org.openbase.bco.dal.lib.layer.service.ServiceProvider; -import org.openbase.bco.dal.lib.layer.service.operation.ActivationStateOperationService; -import org.openbase.bco.dal.lib.layer.service.provider.ActivationStateProviderService; -import org.openbase.bco.dal.lib.state.States; -import org.openbase.bco.dal.remote.action.RemoteAction; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.jul.exception.NotSupportedException; -import org.openbase.jul.exception.printer.ExceptionPrinter; -import org.openbase.jul.exception.printer.LogLevel; -import org.openbase.jul.iface.TimedProcessable; -import org.openbase.jul.schedule.CloseableWriteLockWrapper; -import org.openbase.jul.schedule.FutureProcessor; -import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription; -import org.openbase.type.domotic.action.ActionInitiatorType.ActionInitiator.InitiatorType; -import org.openbase.type.domotic.action.ActionParameterType.ActionParameter; -import org.openbase.type.domotic.action.ActionPriorityType.ActionPriority.Priority; -import org.openbase.type.domotic.service.ServiceTemplateType.ServiceTemplate.ServiceType; -import org.openbase.type.domotic.state.ActivationStateType.ActivationState; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; - -import java.io.Serializable; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -/** - * @param the data type of this unit used for the state synchronization. - * @param the builder used to build the unit data instance. - * - * @author Divine Threepwood - */ -public abstract class AbstractExecutableBaseUnitController> extends AbstractBaseUnitController implements ActivationStateProviderService { - - public static final String FIELD_ACTIVATION_STATE = "activation_state"; - public static final String FIELD_AUTOSTART = "autostart"; - - public AbstractExecutableBaseUnitController(final DB builder) throws org.openbase.jul.exception.InstantiationException { - super(builder); - try { - registerOperationService(ServiceType.ACTIVATION_STATE_SERVICE, new ActivationStateOperationServiceImpl(this)); - } catch (CouldNotPerformException ex) { - throw new InstantiationException(this, ex); - } - } - - @Override - public void activate() throws CouldNotPerformException, InterruptedException { - super.activate(); - handleAutostart(); - } - - @Override - public void deactivate() throws CouldNotPerformException, InterruptedException { - cancelAllActions(); - super.deactivate(); - } - - @Override - public UnitConfig applyConfigUpdate(final UnitConfig config) throws CouldNotPerformException, InterruptedException { - try (final CloseableWriteLockWrapper ignored = getManageWriteLockInterruptible(this)) { - var updatedConfig = super.applyConfigUpdate(config); - handleAutostart(); - return updatedConfig; - } - } - - private boolean detectAutostart() { - try { - return isAutostartEnabled(); - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory(new NotSupportedException("autostart", this, ex), logger, LogLevel.WARN); - return false; - } - } - - private void handleAutostart() { - - if (!detectAutostart()) { - return; - } - - try { - final ActionParameter.Builder actionParameter = ActionDescriptionProcessor.generateDefaultActionParameter(States.Activation.ACTIVE, ServiceType.ACTIVATION_STATE_SERVICE, this); - actionParameter.setInterruptible(true); - actionParameter.setSchedulable(true); - actionParameter.setPriority(Priority.NO); - actionParameter.getActionInitiatorBuilder().setInitiatorType(InitiatorType.SYSTEM); - actionParameter.setExecutionTimePeriod(TimeUnit.MILLISECONDS.toMicros(TimedProcessable.INFINITY_TIMEOUT)); - new RemoteAction(applyAction(actionParameter), this::isActive); - } catch (CouldNotPerformException ex) { - ExceptionPrinter.printHistory("Could not autostart " + this, ex, logger, LogLevel.ERROR); - } - } - - protected abstract boolean isAutostartEnabled() throws CouldNotPerformException; - - protected abstract ActionDescription execute(final ActivationState activationState) throws CouldNotPerformException, InterruptedException; - - protected abstract void stop(final ActivationState activationState) throws CouldNotPerformException, InterruptedException; - - public class ActivationStateOperationServiceImpl implements ActivationStateOperationService { - - private final ServiceProvider serviceProvider; - - public ActivationStateOperationServiceImpl(ServiceProvider serviceProvider) { - this.serviceProvider = serviceProvider; - } - - @Override - public Future setActivationState(final ActivationState activationState) { - try { - - switch (activationState.getValue()) { - case ACTIVE: - applyServiceState(activationState, ServiceType.ACTIVATION_STATE_SERVICE); - execute(activationState); - break; - case INACTIVE: - case UNKNOWN: - stop(activationState); - applyServiceState(activationState, ServiceType.ACTIVATION_STATE_SERVICE); - break; - } - - return FutureProcessor.completedFuture(activationState.getResponsibleAction()); - } catch (CouldNotPerformException | InterruptedException ex) { - return FutureProcessor.canceledFuture(ActionDescription.class, ex); - } - } - - @Override - public ServiceProvider getServiceProvider() { - return serviceProvider; - } - } -} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.kt new file mode 100644 index 0000000000..2141bb02e0 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractExecutableBaseUnitController.kt @@ -0,0 +1,146 @@ +package org.openbase.bco.dal.control.layer.unit + +import com.google.protobuf.AbstractMessage +import org.openbase.bco.dal.lib.action.ActionDescriptionProcessor +import org.openbase.bco.dal.lib.layer.service.ServiceProvider +import org.openbase.bco.dal.lib.layer.service.operation.ActivationStateOperationService +import org.openbase.bco.dal.lib.layer.service.provider.ActivationStateProviderService +import org.openbase.bco.dal.lib.state.States +import org.openbase.bco.dal.remote.action.RemoteAction +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.InstantiationException +import org.openbase.jul.exception.printer.ExceptionPrinter +import org.openbase.jul.exception.printer.LogLevel +import org.openbase.jul.exception.tryOrNull +import org.openbase.jul.iface.TimedProcessable +import org.openbase.jul.schedule.FutureProcessor +import org.openbase.type.domotic.action.ActionDescriptionType.ActionDescription +import org.openbase.type.domotic.action.ActionInitiatorType.ActionInitiator.InitiatorType +import org.openbase.type.domotic.action.ActionPriorityType +import org.openbase.type.domotic.service.ServiceTemplateType +import org.openbase.type.domotic.state.ActivationStateType.ActivationState +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import java.io.Serializable +import java.util.concurrent.Future +import java.util.concurrent.TimeUnit + +/** + * @param the data type of this unit used for the state synchronization. + * @param the builder used to build the unit data instance. + * + * @author [Divine Threepwood](mailto:divine@openbase.org) + */ +abstract class AbstractExecutableBaseUnitController>(builder: DB) : + AbstractBaseUnitController(builder), + ActivationStateProviderService where D : AbstractMessage?, D : Serializable? { + init { + try { + registerOperationService( + ServiceTemplateType.ServiceTemplate.ServiceType.ACTIVATION_STATE_SERVICE, + ActivationStateOperationServiceImpl( + this + ) + ) + } catch (ex: CouldNotPerformException) { + throw InstantiationException(this, ex) + } + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun activate() { + super.activate() + handleAutostart() + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun deactivate() { + cancelAllActions() + super.deactivate() + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun applyConfigUpdate(config: UnitConfig): UnitConfig { + getManageWriteLockInterruptible(this).use { _ -> + val originalConfig = tryOrNull { getConfig() } + return super.applyConfigUpdate(config).also { updatedConfig -> + originalConfig?.let { + if (isAutostartEnabled(it) != isAutostartEnabled(updatedConfig)) { + handleAutostart() + } + } + } + } + } + + private fun handleAutostart() { + if (!isAutostartEnabled()) { + return + } + + try { + val actionParameter = ActionDescriptionProcessor.generateDefaultActionParameter( + States.Activation.ACTIVE, + ServiceTemplateType.ServiceTemplate.ServiceType.ACTIVATION_STATE_SERVICE, + this + ) + actionParameter.setInterruptible(true) + actionParameter.setSchedulable(true) + actionParameter.setPriority(ActionPriorityType.ActionPriority.Priority.NO) + actionParameter.actionInitiatorBuilder.setInitiatorType(InitiatorType.SYSTEM) + actionParameter.setExecutionTimePeriod(TimeUnit.MILLISECONDS.toMicros(TimedProcessable.INFINITY_TIMEOUT)) + RemoteAction(applyAction(actionParameter)) { this.isActive } + } catch (ex: CouldNotPerformException) { + ExceptionPrinter.printHistory("Could not autostart $this", ex, logger, LogLevel.ERROR) + } + } + + protected abstract fun isAutostartEnabled(config: UnitConfig? = tryOrNull { getConfig() }): Boolean + + @Throws(CouldNotPerformException::class, InterruptedException::class) + protected abstract fun execute(activationState: ActivationState): ActionDescription? + + @Throws(CouldNotPerformException::class, InterruptedException::class) + protected abstract fun stop(activationState: ActivationState) + + inner class ActivationStateOperationServiceImpl(private val serviceProvider: ServiceProvider<*>) : + ActivationStateOperationService { + override fun setActivationState(activationState: ActivationState): Future { + try { + when (activationState.value) { + ActivationState.State.ACTIVE -> { + applyServiceState( + activationState, + ServiceTemplateType.ServiceTemplate.ServiceType.ACTIVATION_STATE_SERVICE + ) + execute(activationState) + } + + ActivationState.State.INACTIVE, ActivationState.State.UNKNOWN -> { + stop(activationState) + applyServiceState( + activationState, + ServiceTemplateType.ServiceTemplate.ServiceType.ACTIVATION_STATE_SERVICE + ) + } + + else -> { + throw CouldNotPerformException("Unsupported activation state: ${activationState.value}") + } + } + return FutureProcessor.completedFuture(activationState.responsibleAction) + } catch (ex: CouldNotPerformException) { + return FutureProcessor.canceledFuture( + ActionDescription::class.java, ex + ) + } catch (ex: InterruptedException) { + return FutureProcessor.canceledFuture( + ActionDescription::class.java, ex + ) + } + } + + override fun getServiceProvider(): ServiceProvider<*> { + return serviceProvider + } + } +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractUnitController.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractUnitController.java index 3164d573d3..dfb263371e 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractUnitController.java +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/AbstractUnitController.java @@ -1935,7 +1935,7 @@ public void activate() throws InterruptedException, CouldNotPerformException { * * @throws CouldNotPerformException is thrown if the type of the service is already registered. */ - protected void registerOperationService(final ServiceType serviceType, final OperationService operationService) throws CouldNotPerformException { + final protected void registerOperationService(final ServiceType serviceType, final OperationService operationService) throws CouldNotPerformException { if (operationServiceMap.containsKey(serviceType)) { throw new VerificationFailedException("OperationService for Type[" + serviceType.name() + "] already registered!"); diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AbstractAgentController.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AbstractAgentController.java deleted file mode 100644 index 23cfead1de..0000000000 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AbstractAgentController.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.openbase.bco.dal.control.layer.unit.agent; - -/* - * #%L - * BCO DAL Control - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.bco.dal.control.layer.unit.AbstractAuthorizedBaseUnitController; -import org.openbase.bco.dal.lib.layer.unit.agent.AgentController; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.type.domotic.unit.agent.AgentDataType.AgentData.Builder; -import org.openbase.type.domotic.action.ActionParameterType.ActionParameter; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.agent.AgentClassType.AgentClass; -import org.openbase.type.domotic.unit.agent.AgentDataType; -import org.openbase.type.domotic.unit.agent.AgentDataType.AgentData; - -/** - * @author Divine Threepwood - */ -public abstract class AbstractAgentController extends AbstractAuthorizedBaseUnitController implements AgentController { - - public AbstractAgentController() throws InstantiationException { - super(AgentDataType.AgentData.newBuilder()); - } - - @Override - protected ActionParameter.Builder getActionParameterTemplate(final UnitConfig config) throws InterruptedException, CouldNotPerformException { - final AgentClass agentClass = Registries.getClassRegistry(true).getAgentClassById(config.getAgentConfig().getAgentClassId()); - return ActionParameter.newBuilder() - .addAllCategory(agentClass.getCategoryList()) - .setPriority(agentClass.getPriority()) - .setSchedulable(agentClass.getSchedulable()) - .setInterruptible(agentClass.getInterruptible()); - } - - @Override - protected boolean isAutostartEnabled() throws CouldNotPerformException { - return getConfig().getAgentConfig().getAutostart(); - } -} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AbstractAgentController.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AbstractAgentController.kt new file mode 100644 index 0000000000..5ada887532 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AbstractAgentController.kt @@ -0,0 +1,31 @@ +package org.openbase.bco.dal.control.layer.unit.agent + +import org.openbase.bco.dal.control.layer.unit.AbstractAuthorizedBaseUnitController +import org.openbase.bco.dal.lib.layer.unit.agent.AgentController +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.type.domotic.action.ActionParameterType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig +import org.openbase.type.domotic.unit.agent.AgentDataType + +/** + * @author [Divine Threepwood](mailto:divine@openbase.org) + */ +abstract class AbstractAgentController : + AbstractAuthorizedBaseUnitController( + AgentDataType.AgentData.newBuilder() + ), AgentController { + @Throws(InterruptedException::class, CouldNotPerformException::class) + override fun getActionParameterTemplate(config: UnitConfigType.UnitConfig): ActionParameterType.ActionParameter.Builder { + val agentClass = Registries.getClassRegistry(true).getAgentClassById(config.agentConfig.agentClassId) + return ActionParameterType.ActionParameter.newBuilder() + .addAllCategory(agentClass.categoryList) + .setPriority(agentClass.priority) + .setSchedulable(agentClass.schedulable) + .setInterruptible(agentClass.interruptible) + } + + override fun isAutostartEnabled(config: UnitConfig?): Boolean = + config?.agentConfig?.autostart ?: false +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.java deleted file mode 100644 index bdace7f911..0000000000 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.openbase.bco.dal.control.layer.unit.agent; - -/* - * #%L - * BCO DAL Control - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.bco.dal.lib.layer.unit.agent.Agent; -import org.openbase.bco.dal.lib.layer.unit.agent.AgentController; -import org.openbase.bco.dal.lib.layer.unit.agent.AgentControllerFactory; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.NotAvailableException; -import org.openbase.jul.extension.type.processing.LabelProcessor; -import org.openbase.jul.extension.type.processing.MetaConfigVariableProvider; -import org.openbase.jul.processing.StringProcessor; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.agent.AgentClassType.AgentClass; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.InvocationTargetException; -import java.util.Locale; - -/** - * @author Divine Threepwood - */ -public class AgentControllerFactoryImpl implements AgentControllerFactory { - - protected final Logger logger = LoggerFactory.getLogger(AgentControllerFactoryImpl.class); - private static AgentControllerFactoryImpl instance; - - public static final String META_CONFIG_KEY_AGENT_CLASS_PREFIX = "AGENT_CLASS_PREFIX"; - - private static final String PRESET_AGENT_PACKAGE_PREFIX = "org.openbase.bco.app.preset"; - private static final String CUSTOM_AGENT_PACKAGE_PREFIX = "org.openbase.bco.app"; - - public synchronized static AgentControllerFactoryImpl getInstance() { - if (instance == null) { - instance = new AgentControllerFactoryImpl(); - } - return instance; - } - - private AgentControllerFactoryImpl() { - } - - @Override - public AgentController newInstance(final UnitConfig agentUnitConfig) throws org.openbase.jul.exception.InstantiationException { - AgentController agent; - try { - if (agentUnitConfig == null) { - throw new NotAvailableException("AgentConfig"); - } - - Registries.waitForData(); - final AgentClass agentClass = Registries.getClassRegistry().getAgentClassById(agentUnitConfig.getAgentConfig().getAgentClassId()); - final MetaConfigVariableProvider variableProvider = new MetaConfigVariableProvider("AgentClass", agentClass.getMetaConfig()); - - final String agentClassPrefix = variableProvider.getValue( - META_CONFIG_KEY_AGENT_CLASS_PREFIX, - StringProcessor.removeWhiteSpaces(LabelProcessor.getLabelByLanguage(Locale.ENGLISH, agentClass.getLabel()))); - - try { - // try to load preset agent - String className = PRESET_AGENT_PACKAGE_PREFIX - + ".agent" - + "." + agentClassPrefix + "Agent"; - agent = (AgentController) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance(); - } catch (ClassNotFoundException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | NoSuchMethodException | InvocationTargetException ex) { - // try to load custom agent - String className = CUSTOM_AGENT_PACKAGE_PREFIX - + "." + StringProcessor.removeWhiteSpaces(agentClassPrefix).toLowerCase() - + ".agent" - + "." + StringProcessor.transformToPascalCase(StringProcessor.removeWhiteSpaces(agentClassPrefix)) + "Agent"; - agent = (AgentController) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance(); - } - logger.debug("Creating agent of type [" + LabelProcessor.getBestMatch(agentClass.getLabel()) + "]"); - agent.init(agentUnitConfig); - } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InterruptedException | NoSuchMethodException | InvocationTargetException ex) { - throw new org.openbase.jul.exception.InstantiationException(Agent.class, agentUnitConfig.getId(), ex); - } - return agent; - } -} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.kt new file mode 100644 index 0000000000..18ad1550c5 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.kt @@ -0,0 +1,134 @@ +package org.openbase.bco.dal.control.layer.unit.agent + +import org.openbase.bco.dal.lib.layer.unit.agent.Agent +import org.openbase.bco.dal.lib.layer.unit.agent.AgentController +import org.openbase.bco.dal.lib.layer.unit.agent.AgentControllerFactory +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.NotAvailableException +import org.openbase.jul.extension.type.processing.LabelProcessor.getBestMatch +import org.openbase.jul.extension.type.processing.LabelProcessor.getLabelByLanguage +import org.openbase.jul.extension.type.processing.MetaConfigVariableProvider +import org.openbase.jul.processing.StringProcessor +import org.openbase.type.domotic.unit.UnitConfigType +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.lang.reflect.InvocationTargetException +import java.util.* + +/** + * @author [Divine Threepwood](mailto:divine@openbase.org) + */ +class AgentControllerFactoryImpl private constructor() : AgentControllerFactory { + protected val logger: Logger = LoggerFactory.getLogger(AgentControllerFactoryImpl::class.java) + + @Throws(org.openbase.jul.exception.InstantiationException::class) + override fun newInstance(agentUnitConfig: UnitConfigType.UnitConfig): AgentController { + var agent: AgentController + try { + if (agentUnitConfig == null) { + throw NotAvailableException("AgentConfig") + } + + Registries.waitForData() + val agentClass = Registries.getClassRegistry().getAgentClassById(agentUnitConfig.agentConfig.agentClassId) + val variableProvider = MetaConfigVariableProvider("AgentClass", agentClass.metaConfig) + + val agentClassPrefix = variableProvider.getValue( + META_CONFIG_KEY_AGENT_CLASS_PREFIX, + StringProcessor.removeWhiteSpaces(getLabelByLanguage(Locale.ENGLISH, agentClass.label)) + ) + + try { + // try to load preset agent + val className = (PRESET_AGENT_PACKAGE_PREFIX + + ".agent" + + "." + agentClassPrefix + "Agent") + agent = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AgentController + } catch (ex: ClassNotFoundException) { + // try to load custom agent + val className = (CUSTOM_AGENT_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(agentClassPrefix).lowercase(Locale.getDefault()) + + ".agent" + + "." + StringProcessor.transformToPascalCase(StringProcessor.removeWhiteSpaces(agentClassPrefix)) + "Agent") + agent = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AgentController + } catch (ex: SecurityException) { + val className = (CUSTOM_AGENT_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(agentClassPrefix).lowercase(Locale.getDefault()) + + ".agent" + + "." + StringProcessor.transformToPascalCase(StringProcessor.removeWhiteSpaces(agentClassPrefix)) + "Agent") + agent = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AgentController + } catch (ex: InstantiationException) { + val className = (CUSTOM_AGENT_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(agentClassPrefix).lowercase(Locale.getDefault()) + + ".agent" + + "." + StringProcessor.transformToPascalCase(StringProcessor.removeWhiteSpaces(agentClassPrefix)) + "Agent") + agent = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AgentController + } catch (ex: IllegalAccessException) { + val className = (CUSTOM_AGENT_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(agentClassPrefix).lowercase(Locale.getDefault()) + + ".agent" + + "." + StringProcessor.transformToPascalCase(StringProcessor.removeWhiteSpaces(agentClassPrefix)) + "Agent") + agent = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AgentController + } catch (ex: IllegalArgumentException) { + val className = (CUSTOM_AGENT_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(agentClassPrefix).lowercase(Locale.getDefault()) + + ".agent" + + "." + StringProcessor.transformToPascalCase(StringProcessor.removeWhiteSpaces(agentClassPrefix)) + "Agent") + agent = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AgentController + } catch (ex: NoSuchMethodException) { + val className = (CUSTOM_AGENT_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(agentClassPrefix).lowercase(Locale.getDefault()) + + ".agent" + + "." + StringProcessor.transformToPascalCase(StringProcessor.removeWhiteSpaces(agentClassPrefix)) + "Agent") + agent = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AgentController + } catch (ex: InvocationTargetException) { + val className = (CUSTOM_AGENT_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(agentClassPrefix).lowercase(Locale.getDefault()) + + ".agent" + + "." + StringProcessor.transformToPascalCase(StringProcessor.removeWhiteSpaces(agentClassPrefix)) + "Agent") + agent = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AgentController + } + logger.debug("Creating agent of type [" + getBestMatch(agentClass.label) + "]") + agent.init(agentUnitConfig) + } catch (ex: CouldNotPerformException) { + throw org.openbase.jul.exception.InstantiationException(Agent::class.java, agentUnitConfig.id, ex) + } catch (ex: ClassNotFoundException) { + throw org.openbase.jul.exception.InstantiationException(Agent::class.java, agentUnitConfig.id, ex) + } catch (ex: SecurityException) { + throw org.openbase.jul.exception.InstantiationException(Agent::class.java, agentUnitConfig.id, ex) + } catch (ex: InstantiationException) { + throw org.openbase.jul.exception.InstantiationException(Agent::class.java, agentUnitConfig.id, ex) + } catch (ex: IllegalAccessException) { + throw org.openbase.jul.exception.InstantiationException(Agent::class.java, agentUnitConfig.id, ex) + } catch (ex: IllegalArgumentException) { + throw org.openbase.jul.exception.InstantiationException(Agent::class.java, agentUnitConfig.id, ex) + } catch (ex: InterruptedException) { + throw org.openbase.jul.exception.InstantiationException(Agent::class.java, agentUnitConfig.id, ex) + } catch (ex: NoSuchMethodException) { + throw org.openbase.jul.exception.InstantiationException(Agent::class.java, agentUnitConfig.id, ex) + } catch (ex: InvocationTargetException) { + throw org.openbase.jul.exception.InstantiationException(Agent::class.java, agentUnitConfig.id, ex) + } + return agent + } + + companion object { + @get:Synchronized + var instance: AgentControllerFactoryImpl = AgentControllerFactoryImpl() + private set + + const val META_CONFIG_KEY_AGENT_CLASS_PREFIX: String = "AGENT_CLASS_PREFIX" + + private const val PRESET_AGENT_PACKAGE_PREFIX = "org.openbase.bco.app.preset" + private const val CUSTOM_AGENT_PACKAGE_PREFIX = "org.openbase.bco.app" + } +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerImpl.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerImpl.java deleted file mode 100644 index 20e0e6536f..0000000000 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerImpl.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.openbase.bco.dal.control.layer.unit.agent; - -/* - * #%L - * BCO DAL Control - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - * - * @author Divine Threepwood - * - */ - -import org.openbase.bco.dal.control.layer.unit.UnitControllerRegistrySynchronizer; -import org.openbase.bco.dal.lib.layer.unit.UnitControllerRegistry; -import org.openbase.bco.dal.lib.layer.unit.UnitControllerRegistryImpl; -import org.openbase.bco.dal.lib.layer.unit.agent.AgentController; -import org.openbase.bco.dal.lib.layer.unit.agent.AgentControllerFactory; -import org.openbase.bco.dal.lib.layer.unit.agent.AgentManager; -import org.openbase.bco.registry.remote.login.BCOLogin; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.jul.iface.Launchable; -import org.openbase.jul.iface.VoidInitializable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class AgentManagerImpl implements AgentManager, Launchable, VoidInitializable { - - protected static final Logger LOGGER = LoggerFactory.getLogger(AgentManagerImpl.class); - - private final AgentControllerFactory factory; - private final UnitControllerRegistry agentControllerRegistry; - private final UnitControllerRegistrySynchronizer agentRegistrySynchronizer; - - public AgentManagerImpl() throws InstantiationException { - try { - this.factory = AgentControllerFactoryImpl.getInstance(); - this.agentControllerRegistry = new UnitControllerRegistryImpl<>(); - this.agentRegistrySynchronizer = new UnitControllerRegistrySynchronizer<>(agentControllerRegistry, Registries.getUnitRegistry().getAgentUnitConfigRemoteRegistry(false), factory); - } catch (CouldNotPerformException ex) { - throw new org.openbase.jul.exception.InstantiationException(this, ex); - } - } - - @Override - public void init() { - // this has to stay, else do not implement VoidInitializable - } - - @Override - public void activate() throws CouldNotPerformException, InterruptedException { - BCOLogin.getSession().loginBCOUser(); - agentControllerRegistry.activate(); - agentRegistrySynchronizer.activate(); - } - - @Override - public boolean isActive() { - return agentRegistrySynchronizer.isActive() && - agentControllerRegistry.isActive(); - } - - @Override - public void deactivate() throws CouldNotPerformException, InterruptedException { - agentRegistrySynchronizer.deactivate(); - agentControllerRegistry.deactivate(); - } - - @Override - public void shutdown() { - agentRegistrySynchronizer.shutdown(); - agentControllerRegistry.shutdown(); - } - - @Override - public UnitControllerRegistry getAgentControllerRegistry() { - return agentControllerRegistry; - } -} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerImpl.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerImpl.kt new file mode 100644 index 0000000000..72f0ce2db8 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerImpl.kt @@ -0,0 +1,71 @@ +package org.openbase.bco.dal.control.layer.unit.agent + +import org.openbase.bco.dal.control.layer.unit.UnitControllerRegistrySynchronizer +import org.openbase.bco.dal.lib.layer.unit.UnitControllerRegistry +import org.openbase.bco.dal.lib.layer.unit.UnitControllerRegistryImpl +import org.openbase.bco.dal.lib.layer.unit.agent.AgentController +import org.openbase.bco.dal.lib.layer.unit.agent.AgentControllerFactory +import org.openbase.bco.dal.lib.layer.unit.agent.AgentManager +import org.openbase.bco.registry.remote.Registries +import org.openbase.bco.registry.remote.login.BCOLogin +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.InstantiationException +import org.openbase.jul.iface.Launchable +import org.openbase.jul.iface.VoidInitializable +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +class AgentManagerImpl : AgentManager, Launchable, VoidInitializable { + private var factory: AgentControllerFactory + private var agentControllerRegistry: UnitControllerRegistry? = null + private var agentRegistrySynchronizer: UnitControllerRegistrySynchronizer? = null + + init { + try { + this.factory = AgentControllerFactoryImpl.instance + this.agentControllerRegistry = UnitControllerRegistryImpl() + this.agentRegistrySynchronizer = UnitControllerRegistrySynchronizer( + agentControllerRegistry, + Registries.getUnitRegistry().getAgentUnitConfigRemoteRegistry(false), + factory + ) + } catch (ex: CouldNotPerformException) { + throw InstantiationException(this, ex) + } + } + + override fun init() { + // this has to stay, else do not implement VoidInitializable + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun activate() { + BCOLogin.getSession().loginBCOUser() + agentControllerRegistry!!.activate() + agentRegistrySynchronizer!!.activate() + } + + override fun isActive(): Boolean { + return agentRegistrySynchronizer!!.isActive && + agentControllerRegistry!!.isActive + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun deactivate() { + agentRegistrySynchronizer!!.deactivate() + agentControllerRegistry!!.deactivate() + } + + override fun shutdown() { + agentRegistrySynchronizer!!.shutdown() + agentControllerRegistry!!.shutdown() + } + + override fun getAgentControllerRegistry(): UnitControllerRegistry { + return agentControllerRegistry!! + } + + companion object { + protected val LOGGER: Logger = LoggerFactory.getLogger(AgentManagerImpl::class.java) + } +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerLauncher.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerLauncher.java deleted file mode 100644 index 2d2487c50c..0000000000 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerLauncher.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.openbase.bco.dal.control.layer.unit.agent; - -/* - * #%L - * BCO DAL Control - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - * - * - * @author Divine Threepwood - */ -import org.openbase.bco.dal.lib.layer.unit.agent.AgentManager; -import org.openbase.bco.authentication.lib.BCO; -import org.openbase.jul.pattern.launch.AbstractLauncher; -import org.openbase.jul.exception.InstantiationException; - -public class AgentManagerLauncher extends AbstractLauncher { - - public AgentManagerLauncher() throws InstantiationException { - super(AgentManager.class, AgentManagerImpl.class); - } - - @Override - protected void loadProperties() { - } - - /** - * @param args the command line arguments - */ - public static void main(final String[] args) { - BCO.printLogo(); - AbstractLauncher.main(BCO.class, AgentManager.class, args, AgentManagerLauncher.class); - } -} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerLauncher.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerLauncher.kt new file mode 100644 index 0000000000..7e154aebd5 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentManagerLauncher.kt @@ -0,0 +1,22 @@ +package org.openbase.bco.dal.control.layer.unit.agent + +import org.openbase.bco.authentication.lib.BCO +import org.openbase.bco.dal.lib.layer.unit.agent.AgentManager +import org.openbase.jul.pattern.launch.AbstractLauncher + +class AgentManagerLauncher : + AbstractLauncher(AgentManager::class.java, AgentManagerImpl::class.java) { + override fun loadProperties() { + } + + companion object { + /** + * @param args the command line arguments + */ + @JvmStatic + fun main(args: Array) { + BCO.printLogo() + main(BCO::class.java, AgentManager::class.java, args, AgentManagerLauncher::class.java) + } + } +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AbstractAppController.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AbstractAppController.java deleted file mode 100644 index 68782e8cf7..0000000000 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AbstractAppController.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.openbase.bco.dal.control.layer.unit.app; - -/* - * #%L - * BCO DAL Control - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ -import org.openbase.bco.dal.control.layer.unit.AbstractAuthorizedBaseUnitController; -import org.openbase.bco.dal.lib.layer.service.OperationServiceFactory; -import org.openbase.bco.dal.lib.layer.service.UnitDataSourceFactory; -import org.openbase.bco.dal.lib.layer.unit.UnitController; -import org.openbase.bco.dal.lib.layer.unit.app.AppController; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.NotAvailableException; -import org.openbase.jul.exception.NotSupportedException; -import org.openbase.type.domotic.action.ActionParameterType.ActionParameter; -import org.openbase.type.domotic.unit.app.AppClassType.AppClass; -import org.openbase.type.domotic.unit.app.AppDataType.AppData.Builder; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.app.AppDataType.AppData; - -import java.util.Collections; -import java.util.List; - -/** - * - * * @author Tamino Huxohl - */ -public abstract class AbstractAppController extends AbstractAuthorizedBaseUnitController implements AppController { - - private final OperationServiceFactory operationServiceFactory; - private final UnitDataSourceFactory unitDataSourceFactory; - - public AbstractAppController() throws org.openbase.jul.exception.InstantiationException { - this(null, null); - } - - public AbstractAppController(final OperationServiceFactory operationServiceFactory, final UnitDataSourceFactory unitDataSourceFactory) throws org.openbase.jul.exception.InstantiationException { - super(AppData.newBuilder()); - this.operationServiceFactory = operationServiceFactory; - this.unitDataSourceFactory = unitDataSourceFactory; - } - - @Override - public OperationServiceFactory getOperationServiceFactory() throws NotAvailableException { - if (operationServiceFactory == null) { - throw new NotAvailableException("ServiceFactory", new NotSupportedException("Unit hosting", this)); - } - return operationServiceFactory; - } - - @Override - public UnitDataSourceFactory getUnitDataSourceFactory() throws NotAvailableException { - if (unitDataSourceFactory == null) { - throw new NotAvailableException("UnitDataSourceFactory", new NotSupportedException("UnitDataSource", this)); - } - return unitDataSourceFactory; - } - - @Override - protected ActionParameter.Builder getActionParameterTemplate(final UnitConfig config) throws InterruptedException, CouldNotPerformException { - final AppClass appClass = Registries.getClassRegistry(true).getAppClassById(config.getAppConfig().getAppClassId()); - return ActionParameter.newBuilder() - .addAllCategory(appClass.getCategoryList()) - .setPriority(appClass.getPriority()); - } - - @Override - protected boolean isAutostartEnabled() throws CouldNotPerformException { - return getConfig().getAppConfig().getAutostart(); - } - - @Override - public List> getHostedUnitControllerList() { - // Method can be overwritten in case this app introduces further units. - return Collections.EMPTY_LIST; - } - - @Override - public UnitController getHostedUnitController(String id) throws NotAvailableException { - // Method can be overwritten in case this app introduces further units. - throw new NotAvailableException("UnitController", id); - } - - @Override - public List getHostedUnitConfigList() { - // Method can be overwritten in case this app introduces further units. - return Collections.EMPTY_LIST; - } -} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AbstractAppController.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AbstractAppController.kt new file mode 100644 index 0000000000..114446951c --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AbstractAppController.kt @@ -0,0 +1,63 @@ +package org.openbase.bco.dal.control.layer.unit.app + +import org.openbase.bco.dal.control.layer.unit.AbstractAuthorizedBaseUnitController +import org.openbase.bco.dal.lib.layer.service.OperationServiceFactory +import org.openbase.bco.dal.lib.layer.service.UnitDataSourceFactory +import org.openbase.bco.dal.lib.layer.service.mock.OperationServiceFactoryMock +import org.openbase.bco.dal.lib.layer.unit.Unit +import org.openbase.bco.dal.lib.layer.unit.UnitController +import org.openbase.bco.dal.lib.layer.unit.app.AppController +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.NotAvailableException +import org.openbase.jul.iface.Activatable +import org.openbase.type.domotic.action.ActionParameterType +import org.openbase.type.domotic.unit.UnitConfigType +import org.openbase.type.domotic.unit.app.AppDataType.AppData +import java.util.* + +/** + * * @author [Tamino Huxohl](mailto:pleminoq@openbase.org) + */ +abstract class AbstractAppController( + private val operationServiceFactory: OperationServiceFactory = OperationServiceFactoryMock.getInstance(), + private val unitDataSourceFactory: UnitDataSourceFactory = object : UnitDataSourceFactory { + @Throws(InstantiationException::class) + override fun > newInstance(unit: UNIT): Activatable { + throw org.openbase.jul.exception.InstantiationException( + Unit::class.java, + UnsupportedOperationException("Not supported yet.") + ) + } + }, +) : AbstractAuthorizedBaseUnitController(AppData.newBuilder()), AppController { + + @Throws(NotAvailableException::class) + override fun getOperationServiceFactory(): OperationServiceFactory? = operationServiceFactory + + @Throws(NotAvailableException::class) + override fun getUnitDataSourceFactory(): UnitDataSourceFactory? = unitDataSourceFactory + + @Throws(InterruptedException::class, CouldNotPerformException::class) + override fun getActionParameterTemplate(config: UnitConfigType.UnitConfig): ActionParameterType.ActionParameter.Builder { + val appClass = Registries.getClassRegistry(true).getAppClassById(config.appConfig.appClassId) + return ActionParameterType.ActionParameter.newBuilder() + .addAllCategory(appClass.categoryList) + .setPriority(appClass.priority) + } + + override fun isAutostartEnabled(config: UnitConfigType.UnitConfig?): Boolean = + config?.appConfig?.autostart ?: false + + // Method can be overwritten in case this app introduces further units. + override fun getHostedUnitControllerList(): List?> = emptyList() + + @Throws(NotAvailableException::class) + override fun getHostedUnitController(id: String): UnitController<*, *> { + // Method can be overwritten in case this app introduces further units. + throw NotAvailableException("UnitController", id) + } + + // Method can be overwritten in case this app introduces further units. + override fun getHostedUnitConfigList(): List = emptyList() +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java deleted file mode 100644 index 9c96835574..0000000000 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.java +++ /dev/null @@ -1,96 +0,0 @@ -package org.openbase.bco.dal.control.layer.unit.app; - -/* - * #%L - * BCO DAL Control - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.bco.dal.lib.layer.unit.app.App; -import org.openbase.bco.dal.lib.layer.unit.app.AppController; -import org.openbase.bco.dal.lib.layer.unit.app.AppControllerFactory; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.NotAvailableException; -import org.openbase.jul.extension.type.processing.LabelProcessor; -import org.openbase.jul.processing.StringProcessor; -import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; -import org.openbase.type.domotic.unit.app.AppClassType.AppClass; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.InvocationTargetException; -import java.util.Locale; - -/** - * @author Tamino Huxohl - */ -public class AppControllerFactoryImpl implements AppControllerFactory { - - protected final Logger logger = LoggerFactory.getLogger(AppControllerFactoryImpl.class); - private static AppControllerFactoryImpl instance; - - private static final String PRESET_APP_PACKAGE_PREFIX = "org.openbase.bco.app.preset"; - private static final String CUSTOM_APP_PACKAGE_PREFIX = "org.openbase.bco.app"; - - public synchronized static AppControllerFactoryImpl getInstance() { - if (instance == null) { - instance = new AppControllerFactoryImpl(); - } - return instance; - } - - private AppControllerFactoryImpl() { - } - - @Override - public AppController newInstance(final UnitConfig appUnitConfig) throws org.openbase.jul.exception.InstantiationException { - AppController app; - try { - if (appUnitConfig == null) { - throw new NotAvailableException("AppConfig"); - } - - Registries.waitForData(); - final AppClass appClass = Registries.getClassRegistry().getAppClassById(appUnitConfig.getAppConfig().getAppClassId()); - - try { - // try to load preset app - String className = PRESET_APP_PACKAGE_PREFIX - + "." + StringProcessor.removeWhiteSpaces(LabelProcessor.getLabelByLanguage(Locale.ENGLISH, appClass.getLabel())) + "App"; - app = (AppController) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance(); - } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | - IllegalAccessException | IllegalArgumentException | NoSuchMethodException | - InvocationTargetException ex) { - // try to load custom app - String className = CUSTOM_APP_PACKAGE_PREFIX - + "." + StringProcessor.removeWhiteSpaces(LabelProcessor.getLabelByLanguage(Locale.ENGLISH, appClass.getLabel())).toLowerCase() - + "." + StringProcessor.transformToPascalCase(StringProcessor.removeWhiteSpaces(LabelProcessor.getLabelByLanguage(Locale.ENGLISH, appClass.getLabel()))) + "App"; - app = (AppController) Thread.currentThread().getContextClassLoader().loadClass(className).getConstructor().newInstance(); - } - logger.debug("Creating app of type [" + LabelProcessor.getBestMatch(appClass.getLabel()) + "]"); - app.init(appUnitConfig); - } catch (CouldNotPerformException | ClassNotFoundException | SecurityException | InstantiationException | - IllegalAccessException | IllegalArgumentException | InterruptedException | NoSuchMethodException | - InvocationTargetException ex) { - throw new org.openbase.jul.exception.InstantiationException(App.class, appUnitConfig.getId(), ex); - } - return app; - } -} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.kt new file mode 100644 index 0000000000..2cfe529745 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppControllerFactoryImpl.kt @@ -0,0 +1,196 @@ +package org.openbase.bco.dal.control.layer.unit.app + +import org.openbase.bco.dal.lib.layer.unit.app.App +import org.openbase.bco.dal.lib.layer.unit.app.AppController +import org.openbase.bco.dal.lib.layer.unit.app.AppControllerFactory +import org.openbase.bco.registry.remote.Registries +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.extension.type.processing.LabelProcessor.getBestMatch +import org.openbase.jul.extension.type.processing.LabelProcessor.getLabelByLanguage +import org.openbase.jul.processing.StringProcessor +import org.openbase.type.domotic.unit.UnitConfigType +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.lang.reflect.InvocationTargetException +import java.util.* + +/** + * @author [Tamino Huxohl](mailto:pleminoq@openbase.org) + */ +class AppControllerFactoryImpl private constructor() : AppControllerFactory { + protected val logger: Logger = LoggerFactory.getLogger(AppControllerFactoryImpl::class.java) + + @Throws(org.openbase.jul.exception.InstantiationException::class) + override fun newInstance(appUnitConfig: UnitConfigType.UnitConfig): AppController { + var app: AppController + try { + + Registries.waitForData() + val appClass = Registries.getClassRegistry().getAppClassById(appUnitConfig.appConfig.appClassId) + + try { + // try to load preset app + val className = (PRESET_APP_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces( + getLabelByLanguage( + Locale.ENGLISH, + appClass.label + ) + ) + "App") + app = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AppController + } catch (ex: CouldNotPerformException) { + // try to load custom app + val className = (CUSTOM_APP_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(getLabelByLanguage(Locale.ENGLISH, appClass.label)) + .lowercase( + Locale.getDefault() + ) + + "." + StringProcessor.transformToPascalCase( + StringProcessor.removeWhiteSpaces( + getLabelByLanguage( + Locale.ENGLISH, appClass.label + ) + ) + ) + "App") + app = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AppController + } catch (ex: ClassNotFoundException) { + val className = (CUSTOM_APP_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(getLabelByLanguage(Locale.ENGLISH, appClass.label)) + .lowercase( + Locale.getDefault() + ) + + "." + StringProcessor.transformToPascalCase( + StringProcessor.removeWhiteSpaces( + getLabelByLanguage( + Locale.ENGLISH, appClass.label + ) + ) + ) + "App") + app = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AppController + } catch (ex: SecurityException) { + val className = (CUSTOM_APP_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(getLabelByLanguage(Locale.ENGLISH, appClass.label)) + .lowercase( + Locale.getDefault() + ) + + "." + StringProcessor.transformToPascalCase( + StringProcessor.removeWhiteSpaces( + getLabelByLanguage( + Locale.ENGLISH, appClass.label + ) + ) + ) + "App") + app = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AppController + } catch (ex: InstantiationException) { + val className = (CUSTOM_APP_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(getLabelByLanguage(Locale.ENGLISH, appClass.label)) + .lowercase( + Locale.getDefault() + ) + + "." + StringProcessor.transformToPascalCase( + StringProcessor.removeWhiteSpaces( + getLabelByLanguage( + Locale.ENGLISH, appClass.label + ) + ) + ) + "App") + app = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AppController + } catch (ex: IllegalAccessException) { + val className = (CUSTOM_APP_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(getLabelByLanguage(Locale.ENGLISH, appClass.label)) + .lowercase( + Locale.getDefault() + ) + + "." + StringProcessor.transformToPascalCase( + StringProcessor.removeWhiteSpaces( + getLabelByLanguage( + Locale.ENGLISH, appClass.label + ) + ) + ) + "App") + app = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AppController + } catch (ex: IllegalArgumentException) { + val className = (CUSTOM_APP_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(getLabelByLanguage(Locale.ENGLISH, appClass.label)) + .lowercase( + Locale.getDefault() + ) + + "." + StringProcessor.transformToPascalCase( + StringProcessor.removeWhiteSpaces( + getLabelByLanguage( + Locale.ENGLISH, appClass.label + ) + ) + ) + "App") + app = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AppController + } catch (ex: NoSuchMethodException) { + val className = (CUSTOM_APP_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(getLabelByLanguage(Locale.ENGLISH, appClass.label)) + .lowercase( + Locale.getDefault() + ) + + "." + StringProcessor.transformToPascalCase( + StringProcessor.removeWhiteSpaces( + getLabelByLanguage( + Locale.ENGLISH, appClass.label + ) + ) + ) + "App") + app = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AppController + } catch (ex: InvocationTargetException) { + val className = (CUSTOM_APP_PACKAGE_PREFIX + + "." + StringProcessor.removeWhiteSpaces(getLabelByLanguage(Locale.ENGLISH, appClass.label)) + .lowercase( + Locale.getDefault() + ) + + "." + StringProcessor.transformToPascalCase( + StringProcessor.removeWhiteSpaces( + getLabelByLanguage( + Locale.ENGLISH, appClass.label + ) + ) + ) + "App") + app = Thread.currentThread().contextClassLoader.loadClass(className).getConstructor() + .newInstance() as AppController + } + logger.debug("Creating app of type [" + getBestMatch(appClass.label) + "]") + app.init(appUnitConfig) + } catch (ex: CouldNotPerformException) { + throw org.openbase.jul.exception.InstantiationException(App::class.java, appUnitConfig.id, ex) + } catch (ex: ClassNotFoundException) { + throw org.openbase.jul.exception.InstantiationException(App::class.java, appUnitConfig.id, ex) + } catch (ex: SecurityException) { + throw org.openbase.jul.exception.InstantiationException(App::class.java, appUnitConfig.id, ex) + } catch (ex: InstantiationException) { + throw org.openbase.jul.exception.InstantiationException(App::class.java, appUnitConfig.id, ex) + } catch (ex: IllegalAccessException) { + throw org.openbase.jul.exception.InstantiationException(App::class.java, appUnitConfig.id, ex) + } catch (ex: IllegalArgumentException) { + throw org.openbase.jul.exception.InstantiationException(App::class.java, appUnitConfig.id, ex) + } catch (ex: InterruptedException) { + throw org.openbase.jul.exception.InstantiationException(App::class.java, appUnitConfig.id, ex) + } catch (ex: NoSuchMethodException) { + throw org.openbase.jul.exception.InstantiationException(App::class.java, appUnitConfig.id, ex) + } catch (ex: InvocationTargetException) { + throw org.openbase.jul.exception.InstantiationException(App::class.java, appUnitConfig.id, ex) + } + return app + } + + companion object { + @get:Synchronized + var instance: AppControllerFactoryImpl = AppControllerFactoryImpl() + private set + + private const val PRESET_APP_PACKAGE_PREFIX = "org.openbase.bco.app.preset" + private const val CUSTOM_APP_PACKAGE_PREFIX = "org.openbase.bco.app" + } +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerImpl.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerImpl.java deleted file mode 100644 index de7e844f5b..0000000000 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerImpl.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.openbase.bco.dal.control.layer.unit.app; - -/* - * #%L - * BCO DAL Control - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - */ - -import org.openbase.bco.dal.control.layer.unit.UnitControllerRegistrySynchronizer; -import org.openbase.bco.dal.lib.layer.service.OperationServiceFactory; -import org.openbase.bco.dal.lib.layer.service.UnitDataSourceFactory; -import org.openbase.bco.dal.lib.layer.unit.UnitControllerRegistry; -import org.openbase.bco.dal.lib.layer.unit.UnitControllerRegistryImpl; -import org.openbase.bco.dal.lib.layer.unit.app.AppController; -import org.openbase.bco.dal.lib.layer.unit.app.AppControllerFactory; -import org.openbase.bco.dal.lib.layer.unit.app.AppManager; -import org.openbase.bco.registry.remote.login.BCOLogin; -import org.openbase.bco.registry.remote.Registries; -import org.openbase.jul.exception.CouldNotPerformException; -import org.openbase.jul.exception.InstantiationException; -import org.openbase.jul.exception.NotAvailableException; -import org.openbase.jul.exception.NotSupportedException; -import org.openbase.jul.iface.Launchable; -import org.openbase.jul.iface.VoidInitializable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * - * @author Tamino Huxohl - */ -public class AppManagerImpl implements AppManager, Launchable, VoidInitializable { - - protected static final Logger LOGGER = LoggerFactory.getLogger(AppManagerImpl.class); - - private final AppControllerFactory factory; - private final UnitControllerRegistry appControllerRegistry; - private final UnitControllerRegistrySynchronizer appRegistrySynchronizer; - - public AppManagerImpl() throws InstantiationException { - try { - this.factory = AppControllerFactoryImpl.getInstance(); - this.appControllerRegistry = new UnitControllerRegistryImpl<>(); - this.appRegistrySynchronizer = new UnitControllerRegistrySynchronizer<>(appControllerRegistry, Registries.getUnitRegistry().getAppUnitConfigRemoteRegistry(false),factory); - } catch (CouldNotPerformException ex) { - throw new org.openbase.jul.exception.InstantiationException(this, ex); - } - } - - @Override - public void init() { - // this has to stay, else do not implement VoidInitializable - } - - @Override - public void activate() throws CouldNotPerformException, InterruptedException { - BCOLogin.getSession().loginBCOUser(); - appControllerRegistry.activate(); - appRegistrySynchronizer.activate(); - } - - @Override - public boolean isActive() { - return appRegistrySynchronizer.isActive() && - appControllerRegistry.isActive(); - } - - @Override - public void deactivate() throws CouldNotPerformException, InterruptedException { - appRegistrySynchronizer.deactivate(); - appControllerRegistry.deactivate(); - } - - @Override - public void shutdown() { - appRegistrySynchronizer.shutdown(); - appControllerRegistry.shutdown(); - } - - @Override - public OperationServiceFactory getOperationServiceFactory() throws NotAvailableException { - throw new NotAvailableException("OperationServiceFactory", new NotSupportedException("OperationServiceFactory", this)); - } - - @Override - public UnitDataSourceFactory getUnitDataSourceFactory() throws NotAvailableException { - throw new NotAvailableException("UnitDataSourceFactory", new NotSupportedException("UnitDataSourceFactory", this)); - } - - @Override - public UnitControllerRegistry getAppControllerRegistry() { - return appControllerRegistry; - } - -} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerImpl.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerImpl.kt new file mode 100644 index 0000000000..85e4ea24f3 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerImpl.kt @@ -0,0 +1,89 @@ +package org.openbase.bco.dal.control.layer.unit.app + +import org.openbase.bco.dal.control.layer.unit.UnitControllerRegistrySynchronizer +import org.openbase.bco.dal.lib.layer.service.OperationServiceFactory +import org.openbase.bco.dal.lib.layer.service.UnitDataSourceFactory +import org.openbase.bco.dal.lib.layer.unit.UnitControllerRegistry +import org.openbase.bco.dal.lib.layer.unit.UnitControllerRegistryImpl +import org.openbase.bco.dal.lib.layer.unit.app.AppController +import org.openbase.bco.dal.lib.layer.unit.app.AppControllerFactory +import org.openbase.bco.dal.lib.layer.unit.app.AppManager +import org.openbase.bco.registry.remote.Registries +import org.openbase.bco.registry.remote.login.BCOLogin +import org.openbase.jul.exception.CouldNotPerformException +import org.openbase.jul.exception.InstantiationException +import org.openbase.jul.exception.NotAvailableException +import org.openbase.jul.exception.NotSupportedException +import org.openbase.jul.iface.Launchable +import org.openbase.jul.iface.VoidInitializable +import org.slf4j.Logger +import org.slf4j.LoggerFactory + +/** + * + * @author [Tamino Huxohl](mailto:pleminoq@openbase.org) + */ +class AppManagerImpl : AppManager, Launchable, VoidInitializable { + private var factory: AppControllerFactory? = null + private var appControllerRegistry: UnitControllerRegistry + private var appRegistrySynchronizer: UnitControllerRegistrySynchronizer + + init { + try { + this.factory = AppControllerFactoryImpl.instance + this.appControllerRegistry = UnitControllerRegistryImpl() + this.appRegistrySynchronizer = UnitControllerRegistrySynchronizer( + appControllerRegistry, + Registries.getUnitRegistry().getAppUnitConfigRemoteRegistry(false), + factory + ) + } catch (ex: CouldNotPerformException) { + throw InstantiationException(this, ex) + } + } + + override fun init() { + // this has to stay, else do not implement VoidInitializable + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun activate() { + BCOLogin.getSession().loginBCOUser() + appControllerRegistry.activate() + appRegistrySynchronizer.activate() + } + + override fun isActive(): Boolean { + return appRegistrySynchronizer.isActive && + appControllerRegistry.isActive + } + + @Throws(CouldNotPerformException::class, InterruptedException::class) + override fun deactivate() { + appRegistrySynchronizer.deactivate() + appControllerRegistry.deactivate() + } + + override fun shutdown() { + appRegistrySynchronizer.shutdown() + appControllerRegistry.shutdown() + } + + @Throws(NotAvailableException::class) + override fun getOperationServiceFactory(): OperationServiceFactory { + throw NotAvailableException("OperationServiceFactory", NotSupportedException("OperationServiceFactory", this)) + } + + @Throws(NotAvailableException::class) + override fun getUnitDataSourceFactory(): UnitDataSourceFactory { + throw NotAvailableException("UnitDataSourceFactory", NotSupportedException("UnitDataSourceFactory", this)) + } + + override fun getAppControllerRegistry(): UnitControllerRegistry { + return appControllerRegistry + } + + companion object { + protected val LOGGER: Logger = LoggerFactory.getLogger(AppManagerImpl::class.java) + } +} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.java b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.java deleted file mode 100644 index 169b0f857c..0000000000 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.openbase.bco.dal.control.layer.unit.app; - -import org.openbase.bco.authentication.lib.BCO; -import org.openbase.bco.dal.lib.layer.unit.app.AppManager; -import org.openbase.jul.pattern.launch.AbstractLauncher; - -/* - * #%L - * BCO DAL Control - * %% - * Copyright (C) 2014 - 2021 openbase.org - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this program. If not, see - * . - * #L% - * - * @author Divine Threepwood - * - */ -public class AppManagerLauncher extends AbstractLauncher { - - public AppManagerLauncher() throws org.openbase.jul.exception.InstantiationException { - super(AppManager.class, AppManagerImpl.class); - } - - @Override - public void loadProperties() { - } - - public static void main(String[] args) throws Throwable { - BCO.printLogo(); - main(BCO.class, AppManager.class, args, AppManagerLauncher.class); - } -} diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.kt new file mode 100644 index 0000000000..6ce1870748 --- /dev/null +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/app/AppManagerLauncher.kt @@ -0,0 +1,43 @@ +package org.openbase.bco.dal.control.layer.unit.app + +import org.openbase.bco.authentication.lib.BCO +import org.openbase.bco.dal.lib.layer.unit.app.AppManager +import org.openbase.jul.pattern.launch.AbstractLauncher + +/* +* #%L +* BCO DAL Control +* %% +* Copyright (C) 2014 - 2021 openbase.org +* %% +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program. If not, see +* . +* #L% +* +* @author Divine Threepwood +* +*/ +class AppManagerLauncher : AbstractLauncher(AppManager::class.java, AppManagerImpl::class.java) { + public override fun loadProperties() { + } + + companion object { + @JvmStatic + @Throws(Throwable::class) + fun main(args: Array) { + BCO.printLogo() + main(BCO::class.java, AppManager::class.java, args, AppManagerLauncher::class.java) + } + } +} diff --git a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/UnitConfigProcessor.java b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/UnitConfigProcessor.java index 2afb05b2d0..019a930879 100644 --- a/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/UnitConfigProcessor.java +++ b/module/registry/lib/src/main/java/org/openbase/bco/registry/lib/util/UnitConfigProcessor.java @@ -10,12 +10,12 @@ * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public * License along with this program. If not, see * . @@ -30,17 +30,19 @@ import org.openbase.jul.exception.printer.ExceptionPrinter; import org.openbase.jul.exception.printer.LogLevel; import org.openbase.jul.extension.protobuf.processing.ProtoBufFieldProcessor; -import org.openbase.jul.extension.type.processing.ScopeProcessor; import org.openbase.jul.extension.type.processing.LabelProcessor; import org.openbase.jul.extension.type.processing.ScopeProcessor; import org.openbase.jul.processing.StringProcessor; -import org.slf4j.LoggerFactory; import org.openbase.type.domotic.state.EnablingStateType; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfig; import org.openbase.type.domotic.unit.UnitConfigType.UnitConfigOrBuilder; import org.openbase.type.domotic.unit.UnitTemplateType.UnitTemplate.UnitType; +import org.slf4j.LoggerFactory; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; /** * @author Tamino Huxohl @@ -295,6 +297,11 @@ public static FieldDescriptor getUnitTypeFieldDescriptor(final UnitType unitType * @return the default alias as string. */ public static String getDefaultAlias(final UnitConfigOrBuilder unitConfig, final String alternative) { + + if (unitConfig == null) { + return alternative; + } + try { return getDefaultAlias(unitConfig); } catch (NotAvailableException e) { diff --git a/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt b/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt index 4a9221328e..52a7b39247 100644 --- a/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt +++ b/module/registry/unit-registry/lib/src/main/java/org/openbase/bco/registry/unit/lib/auth/AuthorizationWithTokenHelper.kt @@ -250,24 +250,24 @@ object AuthorizationWithTokenHelper { // check if authenticated user has needed permissions if (AuthorizationHelper.canDo( unitConfig, - userClientPair.getUserId(), + userClientPair.userId, unitRegistry.getAuthorizationGroupMap(), unitRegistry.getLocationMap(), permissionType ) ) { - return AuthPair(userClientPair, userClientPair.getUserId()) + return AuthPair(userClientPair, userClientPair.userId) } if (AuthorizationHelper.canDo( unitConfig, - userClientPair.getClientId(), + userClientPair.clientId, unitRegistry.getAuthorizationGroupMap(), unitRegistry.getLocationMap(), permissionType ) ) { - return AuthPair(userClientPair, userClientPair.getClientId()) + return AuthPair(userClientPair, userClientPair.clientId) } // authenticated user does not have permissions so check if the authorization token grants them @@ -290,11 +290,11 @@ object AuthorizationWithTokenHelper { ) } } - var userRepresentation = userClientPair.getUserId() + var userRepresentation = userClientPair.userId if (userRepresentation.isNotEmpty()) { userRepresentation += "@" } - userRepresentation += userClientPair.getClientId() + userRepresentation += userClientPair.clientId if (userRepresentation.isEmpty()) { userRepresentation = "Other" } From 2ffd7456f781ace5269645a07d1e056679679e1e Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Sun, 24 Mar 2024 20:58:40 +0100 Subject: [PATCH 3/4] shutdown fix --- .../java/org/openbase/bco/dal/control/message/MessageManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt index 1b26e483d1..b0cd960f05 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/message/MessageManager.kt @@ -97,7 +97,7 @@ class MessageManager : Launchable, VoidInitializable { } override fun deactivate() { - Registries.getMessageRegistry().removeDataObserver(messageRegistryChangeObserver) + runCatching { Registries.getMessageRegistry() }.getOrNull()?.removeDataObserver(messageRegistryChangeObserver) unitsOfConditionsLock.write { unitsOfConditions?.forEach { it.removeDataObserver(conditionObserver) } unitsOfConditions = null From c9c888a1bffe3f3cf81c9d455c683790c0b23dbc Mon Sep 17 00:00:00 2001 From: Divine Threepwood Date: Tue, 26 Mar 2024 19:49:50 +0100 Subject: [PATCH 4/4] introduce java compatebility again. --- .../dal/control/layer/unit/agent/AgentControllerFactoryImpl.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.kt b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.kt index 18ad1550c5..79bd946131 100644 --- a/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.kt +++ b/module/dal/control/src/main/java/org/openbase/bco/dal/control/layer/unit/agent/AgentControllerFactoryImpl.kt @@ -123,6 +123,7 @@ class AgentControllerFactoryImpl private constructor() : AgentControllerFactory companion object { @get:Synchronized + @JvmStatic var instance: AgentControllerFactoryImpl = AgentControllerFactoryImpl() private set