diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7b04defeffe..74fa7fb6190 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -78,7 +78,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '16' + node-version: '20' - name: Setup Cache for Node.js uses: actions/cache@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 841b65015f4..8acfebfebf7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -51,7 +51,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: '16' + node-version: '20' - name: Setup Cache for Node.js uses: actions/cache@v4 diff --git a/cnf/checkstyle.xml b/cnf/checkstyle.xml index 7f0ec058035..ad22318becf 100644 --- a/cnf/checkstyle.xml +++ b/cnf/checkstyle.xml @@ -132,7 +132,7 @@ - + diff --git a/cnf/pom.xml b/cnf/pom.xml index 373ce66e9ee..85ce8d8a776 100644 --- a/cnf/pom.xml +++ b/cnf/pom.xml @@ -67,25 +67,25 @@ com.squareup.retrofit2 converter-gson - 2.10.0 + 2.11.0 com.squareup.retrofit2 retrofit - 2.10.0 + 2.11.0 com.squareup.retrofit2 converter-scalars - 2.10.0 + 2.11.0 com.squareup.retrofit2 adapter-rxjava3 - 2.10.0 + 2.11.0 @@ -237,7 +237,7 @@ org.apache.felix org.apache.felix.webconsole - 5.0.0 + 5.0.2 @@ -260,7 +260,7 @@ org.dhatim fastexcel - 0.17.0 + 0.18.0 diff --git a/doc/modules/ROOT/pages/edge/implement.adoc b/doc/modules/ROOT/pages/edge/implement.adoc index 8255cf3f72e..7f899a872e0 100644 --- a/doc/modules/ROOT/pages/edge/implement.adoc +++ b/doc/modules/ROOT/pages/edge/implement.adoc @@ -579,3 +579,11 @@ This functionality can also be used in production environments albeit dynamic co [NOTE] Same applies to the OpenEMS Backend for dynamically loading devices. + +=== Check the Tests of Your Module + +Before continuing with the development and implementation, it's important to ensure that your module's tests are passing. This helps in maintaining the quality and functionality of the module. To check the tests of your module, execute the following Gradle command in the root directory of OpenEMS: + +---- +Run ./gradlew :io.openems.edge.io.shelly:clean in the root Directory to check the tests of your module. +---- diff --git a/io.openems.backend.alerting/src/io/openems/backend/alerting/handler/OfflineEdgeHandler.java b/io.openems.backend.alerting/src/io/openems/backend/alerting/handler/OfflineEdgeHandler.java index 7662a1badcb..dc5f5cf117d 100644 --- a/io.openems.backend.alerting/src/io/openems/backend/alerting/handler/OfflineEdgeHandler.java +++ b/io.openems.backend.alerting/src/io/openems/backend/alerting/handler/OfflineEdgeHandler.java @@ -30,7 +30,7 @@ public class OfflineEdgeHandler implements Handler { public static final int MAX_SIMULTANEOUS_MSGS = 500; public static final int MAX_SIMULTANEOUS_EDGES = 1000; public static final int EDGE_REBOOT_MINUTES = 5; - + private final Logger log = LoggerFactory.getLogger(OfflineEdgeHandler.class); private final int initialDelay; // in Minutes @@ -42,7 +42,7 @@ public class OfflineEdgeHandler implements Handler { private TimedTask initMetadata; private TimedExecutor timeService; - + public OfflineEdgeHandler(MessageSchedulerService mss, TimedExecutor timeService, Mailer mailer, Metadata metadata, int initialDelay) { this.mailer = mailer; @@ -256,7 +256,7 @@ public Consumer getEventHandler(String eventTopic) { yield this::handleOnSetOnline; case Metadata.Events.AFTER_IS_INITIALIZED: - yield this::handleMetadataAfterInitialize; + yield this::handleMetadataAfterInitialize; default: yield null; diff --git a/io.openems.backend.application/BackendApp.bndrun b/io.openems.backend.application/BackendApp.bndrun index bc68a467e50..93014f953ba 100644 --- a/io.openems.backend.application/BackendApp.bndrun +++ b/io.openems.backend.application/BackendApp.bndrun @@ -109,7 +109,7 @@ org.apache.felix.inventory;version='[2.0.0,2.0.1)',\ org.apache.felix.metatype;version='[1.2.4,1.2.5)',\ org.apache.felix.scr;version='[2.2.10,2.2.11)',\ - org.apache.felix.webconsole;version='[5.0.0,5.0.1)',\ + org.apache.felix.webconsole;version='[5.0.2,5.0.3)',\ org.apache.felix.webconsole.plugins.ds;version='[2.3.0,2.3.1)',\ org.jetbrains.kotlin.osgi-bundle;version='[1.9.23,1.9.24)',\ org.jsr-305;version='[3.0.2,3.0.3)',\ diff --git a/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java index 3fd2e3d5b1e..337da975af9 100644 --- a/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java +++ b/io.openems.backend.b2brest/src/io/openems/backend/b2brest/RestHandler.java @@ -1,8 +1,9 @@ package io.openems.backend.b2brest; -import java.io.BufferedReader; +import static io.openems.common.utils.JsonUtils.parseToJsonObject; +import static java.util.stream.Collectors.joining; + import java.io.IOException; -import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.Base64; @@ -11,7 +12,6 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.stream.Collectors; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; @@ -19,7 +19,6 @@ import org.slf4j.LoggerFactory; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import io.openems.backend.common.metadata.User; import io.openems.common.exceptions.OpenemsError; @@ -145,11 +144,12 @@ private void sendErrorResponse(Request baseRequest, HttpServletResponse response */ private static JsonObject parseJson(Request baseRequest) throws OpenemsException { try { - return JsonParser.parseString(// - new BufferedReader(new InputStreamReader(baseRequest.getInputStream())) // - .lines() // - .collect(Collectors.joining("\n"))) // - .getAsJsonObject(); + try (var br = baseRequest.getReader()) { + return parseToJsonObject(br // + .lines() // + .collect(joining("\n"))); + } + } catch (Exception e) { throw new OpenemsException("Unable to parse: " + e.getMessage()); } diff --git a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/SimulationEngine.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/SimulationEngine.java new file mode 100644 index 00000000000..975f1d00ea6 --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/SimulationEngine.java @@ -0,0 +1,24 @@ +package io.openems.backend.common.jsonrpc; + +import java.util.concurrent.CompletableFuture; + +import io.openems.backend.common.jsonrpc.request.SimulationRequest; +import io.openems.backend.common.metadata.User; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; + +public interface SimulationEngine { + + /** + * Handles a JSON-RPC Request. + * + * @param edgeId the Edge-ID + * @param user the authenticated {@link User} + * @param request the {@link JsonrpcRequest} + * @return the JSON-RPC Success Response Future + * @throws OpenemsNamedException on error + */ + public CompletableFuture handleRequest(String edgeId, User user, SimulationRequest request) + throws OpenemsNamedException; + +} \ No newline at end of file diff --git a/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/SimulationRequest.java b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/SimulationRequest.java new file mode 100644 index 00000000000..37d2422e8b4 --- /dev/null +++ b/io.openems.backend.common/src/io/openems/backend/common/jsonrpc/request/SimulationRequest.java @@ -0,0 +1,68 @@ +package io.openems.backend.common.jsonrpc.request; + +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.base.GenericJsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.utils.JsonUtils; + +/** + * Represents a JSON-RPC Request for 'SimulationRequest'. + * + *
+ * {
+ *   "jsonrpc": "2.0",
+ *   "id": "UUID",
+ *   "method": "simulation",
+ *   "params": {
+ *     "payload": {@link JsonrpcRequest},
+ *   }
+ * }
+ * 
+ */ +public class SimulationRequest extends JsonrpcRequest { + + public static final String METHOD = "simulation"; + + /** + * Create {@link SimulationRequest} from a template {@link JsonrpcRequest}. + * + * @param r the template {@link JsonrpcRequest} + * @return the {@link SimulationRequest} + * @throws OpenemsNamedException on parse error + */ + public static SimulationRequest from(JsonrpcRequest r) throws OpenemsNamedException { + var p = r.getParams(); + JsonrpcRequest payload = GenericJsonrpcRequest.from(JsonUtils.getAsJsonObject(p, "payload")); + return new SimulationRequest(r, payload); + } + + private final JsonrpcRequest payload; + + public SimulationRequest(JsonrpcRequest payload) { + super(SimulationRequest.METHOD, payload.getTimeout() /* inherit timeout from payload */); + this.payload = payload; + } + + public SimulationRequest(JsonrpcRequest request, JsonrpcRequest payload) { + super(request, SimulationRequest.METHOD); + this.payload = payload; + } + + /** + * Gets the Payload {@link JsonrpcRequest}. + * + * @return Payload + */ + public JsonrpcRequest getPayload() { + return this.payload; + } + + @Override + public JsonObject getParams() { + return JsonUtils.buildJsonObject() // + .add("payload", this.payload.toJsonObject()) // + .build(); + } +} diff --git a/io.openems.backend.common/src/io/openems/backend/common/metadata/User.java b/io.openems.backend.common/src/io/openems/backend/common/metadata/User.java index 93acdcdd235..2769e26f35a 100644 --- a/io.openems.backend.common/src/io/openems/backend/common/metadata/User.java +++ b/io.openems.backend.common/src/io/openems/backend/common/metadata/User.java @@ -3,10 +3,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Map.Entry; -import com.google.gson.JsonObject; import java.util.NavigableMap; import java.util.TreeMap; +import com.google.gson.JsonObject; + import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; diff --git a/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/SystemLogHandler.java b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/SystemLogHandler.java index 33ad4d777ba..41bd8ce5961 100644 --- a/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/SystemLogHandler.java +++ b/io.openems.backend.edgewebsocket/src/io/openems/backend/edgewebsocket/SystemLogHandler.java @@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory; import com.google.gson.JsonObject; + import io.openems.backend.common.metadata.User; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; diff --git a/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/MetadataDummy.java b/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/MetadataDummy.java index 2a3d132729a..899de4292de 100644 --- a/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/MetadataDummy.java +++ b/io.openems.backend.metadata.dummy/src/io/openems/backend/metadata/dummy/MetadataDummy.java @@ -276,7 +276,7 @@ public EdgeHandler edge() { public Optional getSerialNumberForEdge(Edge edge) { throw new UnsupportedOperationException("DummyMetadata.getSerialNumberForEdge() is not implemented"); } - + @Override public UserAlertingSettings getUserAlertingSettings(String edgeId, String userId) throws OpenemsException { throw new UnsupportedOperationException("DummyMetadata.getUserAlertingSettings() is not implemented"); @@ -369,7 +369,7 @@ public EdgeMetadata getEdgeMetadataForUser(User user, String edgeId) throws Open public Optional getSumState(String edgeId) { throw new UnsupportedOperationException("DummyMetadata.getSumState() is not implemented"); } - + @Override public void logGenericSystemLog(GenericSystemLog systemLog) { this.logInfo(this.log, diff --git a/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/MetadataFile.java b/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/MetadataFile.java index 6ec8f8bc2e4..e1fda5d7540 100644 --- a/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/MetadataFile.java +++ b/io.openems.backend.metadata.file/src/io/openems/backend/metadata/file/MetadataFile.java @@ -310,7 +310,7 @@ public Optional getSerialNumberForEdge(Edge edge) { public UserAlertingSettings getUserAlertingSettings(String edgeId, String userId) throws OpenemsException { throw new UnsupportedOperationException("FileMetadata.getUserAlertingSettings() is not implemented"); } - + @Override public List getUserAlertingSettings(String edgeId) { throw new UnsupportedOperationException("FileMetadata.getUserAlertingSettings() is not implemented"); diff --git a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/OdooHandler.java b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/OdooHandler.java index 625a990b6e7..faf69a04d76 100644 --- a/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/OdooHandler.java +++ b/io.openems.backend.metadata.odoo/src/io/openems/backend/metadata/odoo/odoo/OdooHandler.java @@ -57,7 +57,6 @@ import io.openems.common.jsonrpc.request.GetEdgesRequest.PaginationOptions; import io.openems.common.session.Language; import io.openems.common.session.Role; -import io.openems.common.utils.JsonUtils; import io.openems.common.utils.ObjectUtils; import io.openems.common.utils.PasswordUtils; diff --git a/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnRequest.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnRequest.java index 7464ba22feb..cc4b697ccbb 100644 --- a/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnRequest.java +++ b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/OnRequest.java @@ -20,6 +20,7 @@ import io.openems.backend.common.jsonrpc.request.RegisterUserRequest; import io.openems.backend.common.jsonrpc.request.SetUserAlertingConfigsRequest; import io.openems.backend.common.jsonrpc.request.SetUserInformationRequest; +import io.openems.backend.common.jsonrpc.request.SimulationRequest; import io.openems.backend.common.jsonrpc.request.SubmitSetupProtocolRequest; import io.openems.backend.common.jsonrpc.request.SubscribeEdgesRequest; import io.openems.backend.common.jsonrpc.response.AddEdgeToUserResponse; @@ -224,6 +225,9 @@ private CompletableFuture handleEdgeRpcRequest(WsData wsData, U this.handleSubscribeChannelsRequest(wsData, edgeId, user, SubscribeChannelsRequest.from(request)); case SubscribeSystemLogRequest.METHOD -> this.handleSubscribeSystemLogRequest(wsData, edgeId, user, SubscribeSystemLogRequest.from(request)); + case SimulationRequest.METHOD -> + this.handleSimulationRequest(edgeId, user, SimulationRequest.from(request)); + case ComponentJsonApiRequest.METHOD -> { final var componentRequest = ComponentJsonApiRequest.from(request); if (!"_host".equals(componentRequest.getComponentId())) { @@ -280,6 +284,25 @@ private CompletableFuture handleEdgeRpcRequest(WsData wsData, U return result; } + /** + * Handles a {@link GetSimulationRequest}. + * + * @param edgeId the Edge-ID + * @param user the {@link User} - no specific level required + * @param request the {@link GetSimulationRequest} + * @return the JSON-RPC Success Response Future + * @throws OpenemsNamedException on error + */ + private CompletableFuture handleSimulationRequest(String edgeId, User user, SimulationRequest request) throws OpenemsNamedException { + + final var simulation = this.parent.simulation; + if (simulation == null) { + throw new OpenemsException("simulation unavailable"); + } + + return simulation.handleRequest(edgeId, user, request); + } + private record LogSystemExecuteCommend(// String edgeId, // non-null User user, // non-null diff --git a/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java index 24d90076040..98fe60fb108 100644 --- a/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java +++ b/io.openems.backend.uiwebsocket/src/io/openems/backend/uiwebsocket/impl/UiWebsocketImpl.java @@ -13,6 +13,8 @@ import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.event.Event; import org.osgi.service.event.EventHandler; import org.osgi.service.event.propertytypes.EventTopics; @@ -27,6 +29,7 @@ import io.openems.backend.common.edgewebsocket.EdgeCache; import io.openems.backend.common.edgewebsocket.EdgeWebsocket; import io.openems.backend.common.jsonrpc.JsonRpcRequestHandler; +import io.openems.backend.common.jsonrpc.SimulationEngine; import io.openems.backend.common.metadata.Metadata; import io.openems.backend.common.metadata.User; import io.openems.backend.common.timedata.TimedataManager; @@ -66,6 +69,9 @@ public class UiWebsocketImpl extends AbstractOpenemsBackendComponent @Reference protected volatile TimedataManager timedataManager; + + @Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.OPTIONAL) + protected volatile SimulationEngine simulation; public UiWebsocketImpl() { super("Ui.Websocket"); diff --git a/io.openems.common/src/io/openems/common/OpenemsConstants.java b/io.openems.common/src/io/openems/common/OpenemsConstants.java index 0a1448b2574..4597a187f45 100644 --- a/io.openems.common/src/io/openems/common/OpenemsConstants.java +++ b/io.openems.common/src/io/openems/common/OpenemsConstants.java @@ -22,7 +22,7 @@ public class OpenemsConstants { *

* This is the month of the release. */ - public static final short VERSION_MINOR = 4; + public static final short VERSION_MINOR = 5; /** * The patch version of OpenEMS. diff --git a/io.openems.common/src/io/openems/common/channel/Unit.java b/io.openems.common/src/io/openems/common/channel/Unit.java index 8ad4a2ced40..466ea4fbef6 100644 --- a/io.openems.common/src/io/openems/common/channel/Unit.java +++ b/io.openems.common/src/io/openems/common/channel/Unit.java @@ -187,9 +187,10 @@ public enum Unit { // ########## /** - * Unit of Energy Price [€/MWh]. + * Unit of Energy Price, e.g. [€/MWh]. (see Meta.ChannelId#CURRENCY). */ - EUROS_PER_MEGAWATT_HOUR("€/MWh"), + // TODO symbol should incorporate actual Currency + MONEY_PER_MEGAWATT_HOUR("€/MWh"), // ########## // Frequency @@ -357,7 +358,7 @@ public String format(Object value, OpenemsType type) { case NONE -> // value.toString(); - case AMPERE, DEGREE_CELSIUS, DEZIDEGREE_CELSIUS, EUROS_PER_MEGAWATT_HOUR, HERTZ, MILLIAMPERE, MICROAMPERE, + case AMPERE, DEGREE_CELSIUS, DEZIDEGREE_CELSIUS, MONEY_PER_MEGAWATT_HOUR, HERTZ, MILLIAMPERE, MICROAMPERE, MILLIHERTZ, MILLIVOLT, MICROVOLT, PERCENT, VOLT, VOLT_AMPERE, VOLT_AMPERE_REACTIVE, WATT, KILOWATT, MILLIWATT, WATT_HOURS, OHM, KILOOHM, SECONDS, AMPERE_HOURS, HOUR, CUMULATED_SECONDS, KILOAMPERE_HOURS, KILOVOLT_AMPERE, KILOVOLT_AMPERE_REACTIVE, KILOVOLT_AMPERE_REACTIVE_HOURS, KILOWATT_HOURS, MICROOHM, diff --git a/io.openems.common/src/io/openems/common/exceptions/OpenemsRuntimeException.java b/io.openems.common/src/io/openems/common/exceptions/OpenemsRuntimeException.java new file mode 100644 index 00000000000..d0a67297ec5 --- /dev/null +++ b/io.openems.common/src/io/openems/common/exceptions/OpenemsRuntimeException.java @@ -0,0 +1,28 @@ +package io.openems.common.exceptions; + +public class OpenemsRuntimeException extends RuntimeException { + + private static final long serialVersionUID = -4509666272212124910L; + + public OpenemsRuntimeException() { + super(); + } + + public OpenemsRuntimeException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + + public OpenemsRuntimeException(String message, Throwable cause) { + super(message, cause); + } + + public OpenemsRuntimeException(String message) { + super(message); + } + + public OpenemsRuntimeException(Throwable cause) { + super(cause); + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonArrayPath.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonArrayPath.java new file mode 100644 index 00000000000..99ad2a70858 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonArrayPath.java @@ -0,0 +1,39 @@ +package io.openems.common.jsonrpc.serialization; + +import java.util.List; +import java.util.function.Function; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +public interface JsonArrayPath extends JsonPath { + + /** + * Gets the elements as a list parsed to the object. + * + * @param the type of the objects + * @param mapper the {@link JsonElement} to object mapper + * @return the list with the parsed values + */ + public List getAsList(Function mapper); + + /** + * Gets the elements as a list parsed to the object. + * + * @param the type of the objects + * @param serializer the {@link JsonSerializer} to deserialize the elements + * @return the list with the parsed values + */ + public default List getAsList(JsonSerializer serializer) { + return this.getAsList(serializer::deserializePath); + } + + /** + * Gets the current element of the path. + * + * @return the {@link JsonObject} + */ + public JsonArray get(); + +} \ No newline at end of file diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonArrayPathActual.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonArrayPathActual.java new file mode 100644 index 00000000000..67ac20a1079 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonArrayPathActual.java @@ -0,0 +1,36 @@ +package io.openems.common.jsonrpc.serialization; + +import java.util.List; +import java.util.function.Function; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; + +import io.openems.common.exceptions.OpenemsRuntimeException; +import io.openems.common.utils.JsonUtils; + +public class JsonArrayPathActual implements JsonArrayPath { + + private final JsonArray object; + + public JsonArrayPathActual(JsonElement object) { + if (!object.isJsonArray()) { + throw new OpenemsRuntimeException(object + " is not a JsonArray!"); + } + this.object = object.getAsJsonArray(); + } + + @Override + public List getAsList(Function mapper) { + return JsonUtils.stream(this.object) // + .map(JsonElementPathActual::new) // + .map(mapper) // + .toList(); + } + + @Override + public JsonArray get() { + return this.object; + } + +} \ No newline at end of file diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonArrayPathDummy.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonArrayPathDummy.java new file mode 100644 index 00000000000..bc592b0eacf --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonArrayPathDummy.java @@ -0,0 +1,38 @@ +package io.openems.common.jsonrpc.serialization; + +import static java.util.Collections.emptyList; + +import java.util.List; +import java.util.function.Function; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; + +import io.openems.common.utils.JsonUtils; + +public class JsonArrayPathDummy implements JsonArrayPath, JsonPathDummy { + + private JsonPathDummy elementType; + + @Override + public List getAsList(Function mapper) { + final var path = new JsonElementPathDummy(); + mapper.apply(path); + this.elementType = path; + return emptyList(); + } + + @Override + public JsonArray get() { + return new JsonArray(); + } + + @Override + public JsonElement buildPath() { + return JsonUtils.buildJsonObject() // + .addProperty("type", "array") // + .onlyIf(this.elementType != null, t -> t.add("elementType", this.elementType.buildPath())) // + .build(); + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonElementPath.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonElementPath.java new file mode 100644 index 00000000000..043f8eb3779 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonElementPath.java @@ -0,0 +1,48 @@ +package io.openems.common.jsonrpc.serialization; + +import com.google.gson.JsonElement; + +public interface JsonElementPath extends JsonPath { + + /** + * Gets the current {@link JsonElementPath} as a {@link JsonObjectPath}. + * + * @return the current element as a {@link JsonObjectPath} + */ + public JsonObjectPath getAsJsonObjectPath(); + + /** + * Gets the current {@link JsonElementPath} as a {@link JsonArrayPath}. + * + * @return the current element as a {@link JsonArrayPath} + */ + public JsonArrayPath getAsJsonArrayPath(); + + /** + * Gets the current {@link JsonElementPath} as a {@link StringPath}. + * + * @return the current element as a {@link StringPath} + */ + public StringPath getAsStringPath(); + + /** + * Gets the current {@link JsonElementPath} as a {@link String}. + * + * @return the current element as a {@link String} + */ + public default String getAsString() { + return this.getAsStringPath().get(); + } + + /** + * Gets the current {@link JsonElementPath} as a Object serialized with the + * provided {@link JsonSerializer}. + * + * @param the type of the final object + * @param serializer the {@link JsonSerializer} to deserialize the + * {@link JsonElement} to the object + * @return the current element as a {@link StringPath} + */ + public O getAsObject(JsonSerializer serializer); + +} \ No newline at end of file diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonElementPathActual.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonElementPathActual.java new file mode 100644 index 00000000000..e0d955d1a60 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonElementPathActual.java @@ -0,0 +1,32 @@ +package io.openems.common.jsonrpc.serialization; + +import com.google.gson.JsonElement; + +public class JsonElementPathActual implements JsonElementPath { + private final JsonElement element; + + public JsonElementPathActual(JsonElement element) { + this.element = element; + } + + @Override + public JsonArrayPath getAsJsonArrayPath() { + return new JsonArrayPathActual(this.element); + } + + @Override + public JsonObjectPath getAsJsonObjectPath() { + return new JsonObjectPathActual(this.element); + } + + @Override + public StringPath getAsStringPath() { + return new StringPathActual(this.element); + } + + @Override + public O getAsObject(JsonSerializer deserializer) { + return deserializer.deserializePath(new JsonElementPathActual(this.element)); + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonElementPathDummy.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonElementPathDummy.java new file mode 100644 index 00000000000..5d26b9bffb4 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonElementPathDummy.java @@ -0,0 +1,49 @@ +package io.openems.common.jsonrpc.serialization; + +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; + +public class JsonElementPathDummy implements JsonElementPath, JsonPathDummy { + + private JsonPathDummy dummyPath; + + @Override + public JsonArrayPath getAsJsonArrayPath() { + return this.withDummyPath(new JsonArrayPathDummy()); + } + + @Override + public JsonObjectPath getAsJsonObjectPath() { + return this.withDummyPath(new JsonObjectPathDummy()); + } + + @Override + public StringPath getAsStringPath() { + return this.withDummyPath(new StringPathDummy()); + } + + @Override + public O getAsObject(JsonSerializer deserializer) { + final var dummyPath = new JsonElementPathDummy(); + this.withDummyPath(dummyPath); + return deserializer.deserializePath(dummyPath); + } + + private T withDummyPath(T path) { + if (this.dummyPath != null) { + throw new RuntimeException("Path already set"); + } + this.dummyPath = path; + return path; + } + + public JsonPathDummy getDummyPath() { + return this.dummyPath; + } + + @Override + public JsonElement buildPath() { + return this.dummyPath == null ? JsonNull.INSTANCE : this.dummyPath.buildPath(); + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonObjectPath.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonObjectPath.java new file mode 100644 index 00000000000..fbd925cec08 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonObjectPath.java @@ -0,0 +1,131 @@ +package io.openems.common.jsonrpc.serialization; + +import java.util.List; +import java.util.function.Function; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +public interface JsonObjectPath extends JsonPath { + + /** + * Gets the element associated with the member name from this object. + * + * @param member the name of the member + * @return the {@link JsonElementPath} of the member value + */ + public JsonElementPath getJsonElementPath(String member); + + /** + * Gets the element associated with the member name from this object as a + * {@link StringPath}. + * + * @param member the name of the member + * @return the {@link StringPath} of the member value + */ + public default StringPath getStringPath(String member) { + return this.getJsonElementPath(member).getAsStringPath(); + } + + /** + * Gets the element associated with the member name from this object as a + * {@link String}. + * + * @param member the name of the member + * @return the {@link String} of the member value + */ + public default String getString(String member) { + return this.getStringPath(member).get(); + } + + /** + * Gets the element associated with the member name from this object as a + * {@link JsonObjectPath}. + * + * @param member the name of the member + * @return the {@link JsonObjectPath} of the member value + */ + public default JsonObjectPath getJsonObjectPath(String member) { + return this.getJsonElementPath(member).getAsJsonObjectPath(); + } + + /** + * Gets the element associated with the member name from this object as a + * {@link JsonObject}. + * + * @param member the name of the member + * @return the {@link JsonObject} of the member value + */ + public default JsonObject getJsonObject(String member) { + return this.getJsonObjectPath(member).get(); + } + + /** + * Gets the element associated with the member name from this object as a + * {@link JsonArrayPath}. + * + * @param member the name of the member + * @return the {@link JsonArrayPath} of the member value + */ + public default JsonArrayPath getJsonArrayPath(String member) { + return this.getJsonElementPath(member).getAsJsonArrayPath(); + } + + /** + * Gets the element associated with the member name from this object as a + * {@link JsonArray}. + * + * @param member the name of the member + * @return the {@link JsonArray} of the member value + */ + public default JsonArray getJsonArray(String member) { + return this.getJsonArrayPath(member).get(); + } + + /** + * Gets the element associated with the member name from this object as a + * {@link List}. + * + * @param the type of the elements in the list + * @param member the name of the member + * @param mapper the mapper to deserialize the elements + * @return the {@link List} of the member value + */ + public default List getList(String member, Function mapper) { + return this.getJsonArrayPath(member).getAsList(mapper); + } + + /** + * Gets the element associated with the member name from this object as a + * {@link List}. + * + * @param the type of the elements in the list + * @param member the name of the member + * @param serializer the {@link JsonSerializer} to deserialize the elements + * @return the {@link List} of the member value + */ + public default List getList(String member, JsonSerializer serializer) { + return this.getJsonArrayPath(member).getAsList(serializer); + } + + /** + * Gets the element associated with the member name from this object as the + * generic object. + * + * @param the type of the element + * @param member the name of the member + * @param serializer the {@link JsonSerializer} to deserialize the element + * @return the object of the member value + */ + public default T getElement(String member, JsonSerializer serializer) { + return this.getJsonElementPath(member).getAsObject(serializer); + } + + /** + * Gets the current element of the path. + * + * @return the {@link JsonObject} + */ + public JsonObject get(); + +} \ No newline at end of file diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonObjectPathActual.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonObjectPathActual.java new file mode 100644 index 00000000000..7de86237260 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonObjectPathActual.java @@ -0,0 +1,33 @@ +package io.openems.common.jsonrpc.serialization; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsRuntimeException; + +public class JsonObjectPathActual implements JsonObjectPath { + private final JsonObject object; + + public JsonObjectPathActual(JsonElement object) { + if (!object.isJsonObject()) { + throw new OpenemsRuntimeException(object + " is not a JsonObject!"); + } + this.object = object.getAsJsonObject(); + } + + @Override + public JsonElementPath getJsonElementPath(String member) { + return new JsonElementPathActual(this.object.get(member)); + } + + @Override + public JsonObjectPath getJsonObjectPath(String member) { + return new JsonObjectPathActual(this.object.get(member)); + } + + @Override + public JsonObject get() { + return this.object; + } + +} \ No newline at end of file diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonObjectPathDummy.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonObjectPathDummy.java new file mode 100644 index 00000000000..68639fee6b8 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonObjectPathDummy.java @@ -0,0 +1,47 @@ +package io.openems.common.jsonrpc.serialization; + +import static io.openems.common.utils.JsonUtils.toJsonObject; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +import io.openems.common.utils.JsonUtils; + +public class JsonObjectPathDummy implements JsonObjectPath, JsonPathDummy { + + private final Map paths = new TreeMap<>(); + + @Override + public JsonElementPath getJsonElementPath(String member) { + return this.withDummyPath(member, new JsonElementPathDummy()); + } + + @Override + public JsonObjectPath getJsonObjectPath(String member) { + return this.withDummyPath(member, new JsonObjectPathDummy()); + } + + @Override + public JsonObject get() { + return new JsonObject(); + } + + @Override + public JsonElement buildPath() { + return JsonUtils.buildJsonObject() // + .addProperty("type", "object") // + .add("properties", this.paths.entrySet().stream() // + .collect(toJsonObject(Entry::getKey, input -> input.getValue().buildPath()))) // + .build(); + } + + private final T withDummyPath(String member, T path) { + this.paths.put(member, path); + return path; + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonPath.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonPath.java new file mode 100644 index 00000000000..4c9b0d82a59 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonPath.java @@ -0,0 +1,5 @@ +package io.openems.common.jsonrpc.serialization; + +public interface JsonPath { + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonPathDummy.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonPathDummy.java new file mode 100644 index 00000000000..3148bab82e0 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonPathDummy.java @@ -0,0 +1,14 @@ +package io.openems.common.jsonrpc.serialization; + +import com.google.gson.JsonElement; + +public interface JsonPathDummy { + + /** + * Creates the description of the Path as a {@link JsonElement}. + * + * @return the created {@link JsonElement} + */ + public JsonElement buildPath(); + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonSerializer.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonSerializer.java new file mode 100644 index 00000000000..2b8e52df570 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonSerializer.java @@ -0,0 +1,41 @@ +package io.openems.common.jsonrpc.serialization; + +import com.google.gson.JsonElement; + +public interface JsonSerializer { + + /** + * Gets the {@link SerializerDescriptor} of the object this serializer + * serializes. + * + * @return the {@link SerializerDescriptor} + */ + public SerializerDescriptor descriptor(); + + /** + * Serializes from a object to a {@link JsonElement}. + * + * @param obj the object to serialize + * @return the serialized object as a {@link JsonElement} + */ + public JsonElement serialize(T obj); + + /** + * Deserializes from a {@link JsonElement} to the object. + * + * @param json the {@link JsonElement} to deserialize into a object + * @return the deserialized object from the {@link JsonElement} + */ + public T deserializePath(JsonElementPath json); + + /** + * Deserializes from a {@link JsonElement} to the object. + * + * @param json the {@link JsonElement} to deserialize into a object + * @return the deserialized object from the {@link JsonElement} + */ + public default T deserialize(JsonElement json) { + return this.deserializePath(new JsonElementPathActual(json)); + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonSerializerUtil.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonSerializerUtil.java new file mode 100644 index 00000000000..2052d35d0b8 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/JsonSerializerUtil.java @@ -0,0 +1,157 @@ +package io.openems.common.jsonrpc.serialization; + +import java.util.function.Function; + +import com.google.common.base.Supplier; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +public final class JsonSerializerUtil { + + /** + * Creates a {@link JsonSerializer} for a empty {@link JsonObject}. + * + * @param the type of the object + * @param object the object supplier to create an empty instance of it + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer emptyObjectSerializer(// + Supplier object // + ) { + return jsonObjectSerializer(json -> object.get(), json -> new JsonObject()); + } + + /** + * Creates a {@link JsonSerializer} for the provided type. + * + * @param the type of the object to serialize and deserialize. + * @param clazz the {@link Class} of the object + * @param toObjMapper the deserializer from {@link JsonObject} to object + * @param toJsonMapper the serializer from object to {@link JsonElement} + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer jsonObjectSerializer(// + Class clazz, // + Function toObjMapper, // + Function toJsonMapper // + ) { + return jsonObjectSerializer(toObjMapper, toJsonMapper); + } + + /** + * Creates a {@link JsonSerializer} for the provided type. + * + * @param the type of the object to serialize and deserialize. + * @param toObjMapper the deserializer from {@link JsonObject} to object + * @param toJsonMapper the serializer from object to {@link JsonElement} + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer jsonObjectSerializer(// + Function toObjMapper, // + Function toJsonMapper // + ) { + return jsonSerializer(toObjMapper.compose(JsonElementPath::getAsJsonObjectPath), toJsonMapper); + } + + /** + * Creates a {@link JsonSerializer} for the provided type. + * + * @param the type of the object to serialize and deserialize. + * @param clazz the {@link Class} of the object + * @param toObjMapper the deserializer from {@link JsonArray} to object + * @param toJsonMapper the serializer from object to {@link JsonElement} + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer jsonArraySerializer(// + Class clazz, // + Function toObjMapper, // + Function toJsonMapper // + ) { + return jsonArraySerializer(toObjMapper, toJsonMapper); + } + + /** + * Creates a {@link JsonSerializer} for the provided type. + * + * @param the type of the object to serialize and deserialize. + * @param toObjMapper the deserializer from {@link JsonArray} to object + * @param toJsonMapper the serializer from object to {@link JsonElement} + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer jsonArraySerializer(// + Function toObjMapper, // + Function toJsonMapper // + ) { + return jsonSerializer(toObjMapper.compose(JsonElementPath::getAsJsonArrayPath), toJsonMapper); + } + + /** + * Creates a {@link JsonSerializer} for the provided type. + * + * @param the type of the object to serialize and deserialize. + * @param clazz the {@link Class} of the object + * @param toObjMapper the deserializer from {@link JsonElement} to object + * @param toJsonMapper the serializer from object ot {@link JsonElement} + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer jsonSerializer(// + Class clazz, // + Function toObjMapper, // + Function toJsonMapper // + ) { + return jsonSerializer(toObjMapper, toJsonMapper); + } + + /** + * Creates a {@link JsonSerializer} for the provided type. + * + * @param the type of the object to serialize and deserialize. + * @param toObjMapper the deserializer from {@link JsonElement} to object + * @param toJsonMapper the serializer from object to {@link JsonElement} + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer jsonSerializer(// + Function toObjMapper, // + Function toJsonMapper // + ) { + final var path = new JsonElementPathDummy(); + toObjMapper.apply(path); + return new SimpleJsonSerializer(new SerializerDescriptor(path), toJsonMapper, toObjMapper); + } + + private JsonSerializerUtil() { + } + + private static final class SimpleJsonSerializer implements JsonSerializer { + + private final SerializerDescriptor descriptor; + private final Function serialize; + private final Function deserialize; + + public SimpleJsonSerializer(SerializerDescriptor descriptor, Function serialize, + Function deserialize) { + super(); + this.descriptor = descriptor; + this.serialize = serialize; + this.deserialize = deserialize; + } + + @Override + public SerializerDescriptor descriptor() { + return this.descriptor; + } + + @Override + public JsonElement serialize(T a) { + return this.serialize.apply(a); + } + + @Override + public T deserializePath(JsonElementPath a) { + return this.deserialize.apply(a); + } + + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/SerializerDescriptor.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/SerializerDescriptor.java new file mode 100644 index 00000000000..568cf1a2c09 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/SerializerDescriptor.java @@ -0,0 +1,26 @@ +package io.openems.common.jsonrpc.serialization; + +import com.google.gson.JsonElement; + +public class SerializerDescriptor { + + private final JsonElementPathDummy obj; + + public SerializerDescriptor(JsonElementPathDummy obj) { + this.obj = obj; + } + + /** + * Creates a {@link JsonElement} of the object description. + * + * @return the created {@link JsonElementPath} + */ + public JsonElement toJson() { + return this.obj.buildPath(); + } + + public JsonElementPathDummy getObj() { + return this.obj; + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/StringPath.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/StringPath.java new file mode 100644 index 00000000000..d32c4056eb0 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/StringPath.java @@ -0,0 +1,21 @@ +package io.openems.common.jsonrpc.serialization; + +import java.util.UUID; + +public interface StringPath extends JsonPath { + + /** + * Gets the string value of the current path. + * + * @return the value + */ + public String get(); + + /** + * Gets the value as a {@link UUID}. + * + * @return the {@link UUID} + */ + public UUID getAsUuid(); + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/StringPathActual.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/StringPathActual.java new file mode 100644 index 00000000000..d0f2e2799b1 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/StringPathActual.java @@ -0,0 +1,32 @@ +package io.openems.common.jsonrpc.serialization; + +import java.util.UUID; + +import com.google.gson.JsonElement; + +import io.openems.common.exceptions.OpenemsRuntimeException; + +public class StringPathActual implements StringPath { + + private final String element; + + public StringPathActual(JsonElement element) throws OpenemsRuntimeException { + super(); + if (!element.isJsonPrimitive() // + || !element.getAsJsonPrimitive().isString()) { + throw new OpenemsRuntimeException(element + " is not a String!"); + } + this.element = element.getAsString(); + } + + @Override + public String get() { + return this.element; + } + + @Override + public UUID getAsUuid() { + return UUID.fromString(this.element); + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/StringPathDummy.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/StringPathDummy.java new file mode 100644 index 00000000000..5cd61d65a25 --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/StringPathDummy.java @@ -0,0 +1,28 @@ +package io.openems.common.jsonrpc.serialization; + +import java.util.UUID; + +import com.google.gson.JsonElement; + +import io.openems.common.utils.JsonUtils; + +public class StringPathDummy implements StringPath, JsonPathDummy { + + @Override + public String get() { + return ""; + } + + @Override + public UUID getAsUuid() { + return UUID.randomUUID(); + } + + @Override + public JsonElement buildPath() { + return JsonUtils.buildJsonObject() // + .addProperty("type", "string") // + .build(); + } + +} diff --git a/io.openems.common/src/io/openems/common/jsonrpc/serialization/package-info.java b/io.openems.common/src/io/openems/common/jsonrpc/serialization/package-info.java new file mode 100644 index 00000000000..5d8dc7bba5c --- /dev/null +++ b/io.openems.common/src/io/openems/common/jsonrpc/serialization/package-info.java @@ -0,0 +1,3 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +@org.osgi.annotation.bundle.Export +package io.openems.common.jsonrpc.serialization; diff --git a/io.openems.common/src/io/openems/common/types/EdgeConfig.java b/io.openems.common/src/io/openems/common/types/EdgeConfig.java index 1871fb826d7..232f2de63f0 100644 --- a/io.openems.common/src/io/openems/common/types/EdgeConfig.java +++ b/io.openems.common/src/io/openems/common/types/EdgeConfig.java @@ -1254,7 +1254,7 @@ public static EdgeConfig fromJson(JsonObject json) { private volatile JsonObject _json = null; /** - * Build from {@link ActualEdgeConfig} using a {@link Builder}. + * Build from {@link ActualEdgeConfig}. * * @param actual the {@link ActualEdgeConfig} */ diff --git a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketClient.java b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketClient.java index 71b9cbda1e2..79c805a69c3 100644 --- a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketClient.java +++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketClient.java @@ -10,6 +10,7 @@ import org.java_websocket.drafts.Draft; import org.java_websocket.drafts.Draft_6455; import org.java_websocket.exceptions.WebsocketNotConnectedException; +import org.java_websocket.extensions.permessage_deflate.PerMessageDeflateExtension; import org.java_websocket.framing.CloseFrame; import org.java_websocket.handshake.ServerHandshake; import org.slf4j.Logger; @@ -34,7 +35,7 @@ public abstract class AbstractWebsocketClient extends Abstract public static final Map NO_HTTP_HEADERS = new HashMap<>(); public static final Proxy NO_PROXY = null; - public static final Draft DEFAULT_DRAFT = new Draft_6455(); + public static final Draft DEFAULT_DRAFT = new Draft_6455(new PerMessageDeflateExtension()); protected final WebSocketClient ws; diff --git a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java index 07bd5a40d8a..72880facf42 100644 --- a/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java +++ b/io.openems.common/src/io/openems/common/websocket/AbstractWebsocketServer.java @@ -14,7 +14,10 @@ import java.util.function.Function; import org.java_websocket.WebSocket; +import org.java_websocket.drafts.Draft; +import org.java_websocket.drafts.Draft_6455; import org.java_websocket.exceptions.WebsocketNotConnectedException; +import org.java_websocket.extensions.permessage_deflate.PerMessageDeflateExtension; import org.java_websocket.handshake.ClientHandshake; import org.java_websocket.server.WebSocketServer; import org.slf4j.Logger; @@ -63,6 +66,7 @@ public boolean isAtLeast(DebugMode other) { private final WebSocketServer ws; private final DebugMode debugMode; private final Collection connections = ConcurrentHashMap.newKeySet(); + private final Draft perMessageDeflateDraft = new Draft_6455(new PerMessageDeflateExtension()); /** * Construct an {@link AbstractWebsocketServer}. @@ -80,7 +84,7 @@ protected AbstractWebsocketServer(String name, int port, int poolSize, DebugMode this.port = port; this.ws = new WebSocketServer(new InetSocketAddress(port), /* AVAILABLE_PROCESSORS */ Runtime.getRuntime().availableProcessors(), // - /* drafts, no filter */ Collections.emptyList(), // + /* enable perMessageDeflate */ Collections.singletonList(this.perMessageDeflateDraft), // this.connections) { @Override diff --git a/io.openems.edge.application/EdgeApp.bndrun b/io.openems.edge.application/EdgeApp.bndrun index 09c9967dc78..0e5c289c8f1 100644 --- a/io.openems.edge.application/EdgeApp.bndrun +++ b/io.openems.edge.application/EdgeApp.bndrun @@ -105,6 +105,7 @@ bnd.identity;id='io.openems.edge.controller.symmetric.timeslotpeakshaving',\ bnd.identity;id='io.openems.edge.core',\ bnd.identity;id='io.openems.edge.edge2edge',\ + bnd.identity;id='io.openems.edge.energy',\ bnd.identity;id='io.openems.edge.ess.adstec.storaxe',\ bnd.identity;id='io.openems.edge.ess.byd.container',\ bnd.identity;id='io.openems.edge.ess.cluster',\ @@ -149,9 +150,9 @@ bnd.identity;id='io.openems.edge.meter.camillebauer.aplus',\ bnd.identity;id='io.openems.edge.meter.carlo.gavazzi.em300',\ bnd.identity;id='io.openems.edge.meter.discovergy',\ + bnd.identity;id='io.openems.edge.meter.eastron',\ bnd.identity;id='io.openems.edge.meter.janitza',\ bnd.identity;id='io.openems.edge.meter.kdk',\ - bnd.identity;id='io.openems.edge.meter.microcare.sdm630',\ bnd.identity;id='io.openems.edge.meter.phoenixcontact',\ bnd.identity;id='io.openems.edge.meter.plexlog',\ bnd.identity;id='io.openems.edge.meter.pqplus',\ @@ -270,6 +271,8 @@ io.openems.edge.controller.symmetric.timeslotpeakshaving;version=snapshot,\ io.openems.edge.core;version=snapshot,\ io.openems.edge.edge2edge;version=snapshot,\ + io.openems.edge.energy;version=snapshot,\ + io.openems.edge.energy.api;version=snapshot,\ io.openems.edge.ess.adstec.storaxe;version=snapshot,\ io.openems.edge.ess.api;version=snapshot,\ io.openems.edge.ess.byd.container;version=snapshot,\ @@ -318,9 +321,9 @@ io.openems.edge.meter.camillebauer.aplus;version=snapshot,\ io.openems.edge.meter.carlo.gavazzi.em300;version=snapshot,\ io.openems.edge.meter.discovergy;version=snapshot,\ + io.openems.edge.meter.eastron;version=snapshot,\ io.openems.edge.meter.janitza;version=snapshot,\ io.openems.edge.meter.kdk;version=snapshot,\ - io.openems.edge.meter.microcare.sdm630;version=snapshot,\ io.openems.edge.meter.phoenixcontact;version=snapshot,\ io.openems.edge.meter.plexlog;version=snapshot,\ io.openems.edge.meter.pqplus;version=snapshot,\ @@ -394,7 +397,7 @@ org.apache.felix.inventory;version='[2.0.0,2.0.1)',\ org.apache.felix.metatype;version='[1.2.4,1.2.5)',\ org.apache.felix.scr;version='[2.2.10,2.2.11)',\ - org.apache.felix.webconsole;version='[5.0.0,5.0.1)',\ + org.apache.felix.webconsole;version='[5.0.2,5.0.3)',\ org.apache.felix.webconsole.plugins.ds;version='[2.3.0,2.3.1)',\ org.eclipse.jetty.client;version='[9.4.28,9.4.29)',\ org.eclipse.jetty.http;version='[9.4.28,9.4.29)',\ diff --git a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBatteryImpl.java b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBatteryImpl.java index 20737cf8ab3..7c660f82094 100644 --- a/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBatteryImpl.java +++ b/io.openems.edge.battery.bmw/src/io/openems/edge/battery/bmw/BmwBatteryImpl.java @@ -27,7 +27,6 @@ import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.battery.api.Battery; import io.openems.edge.battery.bmw.enums.BmsState; import io.openems.edge.battery.bmw.enums.State; @@ -342,10 +341,8 @@ private void setStateMachineState(State state) { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { - + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // - new FC16WriteRegistersTask(1399, // m(BmwBattery.ChannelId.HEART_BEAT, new UnsignedWordElement(1399)), // m(BmwBattery.ChannelId.BMS_STATE_COMMAND, new UnsignedWordElement(1400)), // diff --git a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130Impl.java b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130Impl.java index c9ee669d9f7..0dae16c2fcd 100644 --- a/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130Impl.java +++ b/io.openems.edge.battery.bydcommercial/src/io/openems/edge/battery/bydcommercial/BydBatteryBoxCommercialC130Impl.java @@ -3,6 +3,7 @@ import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.DIRECT_1_TO_1; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @@ -26,7 +27,6 @@ import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.battery.api.Battery; import io.openems.edge.battery.bydcommercial.statemachine.Context; import io.openems.edge.battery.bydcommercial.statemachine.StateMachine; @@ -176,7 +176,7 @@ public String debugLog() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(0x2010, Priority.HIGH, // m(BydBatteryBoxCommercialC130.ChannelId.POWER_CIRCUIT_CONTROL, new UnsignedWordElement(0x2010)) // @@ -964,43 +964,30 @@ public StartStop getStartStopTarget() { BydBatteryBoxCommercialC130Impl.this.isModbusProtocolInitialized = true; // Try to read MODULE_QTY Register - try { - ModbusUtils.readELementOnce(this.getModbusProtocol(), new UnsignedWordElement(0x210D), false) - .thenAccept(moduleQtyValue -> { - if (moduleQtyValue != null) { - // Register is available -> add Registers for current hardware to protocol - try { - this.getModbusProtocol().addTasks(// - new FC3ReadRegistersTask(0x210D, Priority.LOW, // - m(BydBatteryBoxCommercialC130.ChannelId.MODULE_QTY, - new UnsignedWordElement(0x210D)), // - m(BydBatteryBoxCommercialC130.ChannelId.TOTAL_VOLTAGE_OF_SINGLE_MODULE, - new UnsignedWordElement(0x210E))), // - new FC3ReadRegistersTask(0x216E, Priority.LOW, // - m(Battery.ChannelId.CHARGE_MAX_VOLTAGE, new UnsignedWordElement(0x216E), // - SCALE_FACTOR_MINUS_1), // - m(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE, - new UnsignedWordElement(0x216F), // - SCALE_FACTOR_MINUS_1) // - )); - } catch (OpenemsException e) { - BydBatteryBoxCommercialC130Impl.this.logError(BydBatteryBoxCommercialC130Impl.this.log, - "Unable to add registers for detected hardware version: " + e.getMessage()); - e.printStackTrace(); - } // - } else { - BydBatteryBoxCommercialC130Impl.this.logInfo(BydBatteryBoxCommercialC130Impl.this.log, - "Detected old hardware version. Registers are not available. Setting default values."); - - this._setChargeMaxVoltage(OLD_VERSION_DEFAULT_CHARGE_MAX_VOLTAGE); - this._setDischargeMinVoltage(OLD_VERSION_DEFAULT_DISCHARGE_MIN_VOLTAGE); - } - }); - } catch (OpenemsException e) { - BydBatteryBoxCommercialC130Impl.this.logError(BydBatteryBoxCommercialC130Impl.this.log, - "Unable to detect hardware version: " + e.getMessage()); - e.printStackTrace(); - } + readElementOnce(this.getModbusProtocol(), ModbusUtils::doNotRetry, new UnsignedWordElement(0x210D)) + .thenAccept(moduleQtyValue -> { + if (moduleQtyValue != null) { + // Register is available -> add Registers for current hardware to protocol + this.getModbusProtocol().addTasks(// + new FC3ReadRegistersTask(0x210D, Priority.LOW, // + m(BydBatteryBoxCommercialC130.ChannelId.MODULE_QTY, + new UnsignedWordElement(0x210D)), // + m(BydBatteryBoxCommercialC130.ChannelId.TOTAL_VOLTAGE_OF_SINGLE_MODULE, + new UnsignedWordElement(0x210E))), // + new FC3ReadRegistersTask(0x216E, Priority.LOW, // + m(Battery.ChannelId.CHARGE_MAX_VOLTAGE, new UnsignedWordElement(0x216E), // + SCALE_FACTOR_MINUS_1), // + m(Battery.ChannelId.DISCHARGE_MIN_VOLTAGE, new UnsignedWordElement(0x216F), // + SCALE_FACTOR_MINUS_1) // + )); + } else { + BydBatteryBoxCommercialC130Impl.this.logInfo(BydBatteryBoxCommercialC130Impl.this.log, + "Detected old hardware version. Registers are not available. Setting default values."); + + this._setChargeMaxVoltage(OLD_VERSION_DEFAULT_CHARGE_MAX_VOLTAGE); + this._setDischargeMinVoltage(OLD_VERSION_DEFAULT_DISCHARGE_MIN_VOLTAGE); + } + }); }; } diff --git a/io.openems.edge.battery.fenecon.commercial/src/io/openems/edge/battery/fenecon/commercial/BatteryFeneconCommercialImpl.java b/io.openems.edge.battery.fenecon.commercial/src/io/openems/edge/battery/fenecon/commercial/BatteryFeneconCommercialImpl.java index d04da464d2d..869077dbe64 100644 --- a/io.openems.edge.battery.fenecon.commercial/src/io/openems/edge/battery/fenecon/commercial/BatteryFeneconCommercialImpl.java +++ b/io.openems.edge.battery.fenecon.commercial/src/io/openems/edge/battery/fenecon/commercial/BatteryFeneconCommercialImpl.java @@ -181,7 +181,7 @@ protected void deactivate() { }); @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // // Versions new FC3ReadRegistersTask(0, Priority.LOW, // diff --git a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImpl.java b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImpl.java index d45e42a80b6..01748d00999 100644 --- a/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImpl.java +++ b/io.openems.edge.battery.fenecon.home/src/io/openems/edge/battery/fenecon/home/BatteryFeneconHomeImpl.java @@ -2,6 +2,7 @@ import static io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent.BitConverter.INVERT; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; import java.util.List; import java.util.Objects; @@ -186,7 +187,7 @@ private void handleStateMachine() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(500, Priority.LOW, // m(new BitsWordElement(500, this) // @@ -345,7 +346,7 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { */ private void detectHardwareType() throws OpenemsException { // Set Battery-Protection - ModbusUtils.readELementOnce(this.getModbusProtocol(), new UnsignedWordElement(10019), true) // + readElementOnce(this.getModbusProtocol(), ModbusUtils::retryOnNull, new UnsignedWordElement(10019)) .thenAccept(value -> { if (value == null) { return; diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionBImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionBImpl.java index 102b6e8481a..771573775ef 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionBImpl.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionb/BatterySoltaroClusterVersionBImpl.java @@ -509,7 +509,7 @@ public void setStateMachineState(State state) { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var protocol = new ModbusProtocol(this, // -------- control registers of master -------------------------------------- new FC16WriteRegistersTask(0x1004, // diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImpl.java index d51be44a2c3..56c3d916474 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImpl.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/cluster/versionc/BatterySoltaroClusterVersionCImpl.java @@ -2,6 +2,7 @@ import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; import java.util.LinkedList; import java.util.Optional; @@ -10,6 +11,7 @@ import java.util.TreeSet; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiPredicate; import java.util.function.Consumer; import org.osgi.service.cm.ConfigurationAdmin; @@ -57,6 +59,7 @@ import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; import io.openems.edge.bridge.modbus.api.task.FC6WriteRegisterTask; +import io.openems.edge.bridge.modbus.api.task.Task.ExecuteState; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.EnumReadChannel; import io.openems.edge.common.channel.IntegerReadChannel; @@ -185,198 +188,191 @@ protected void deactivate() { private void updateRackChannels(Integer numberOfModules, TreeSet racks) throws OpenemsException { for (Rack r : racks) { - try { - this.getModbusProtocol().addTasks(// - - new FC3ReadRegistersTask(r.offset + 0x000B, Priority.LOW, // - m(this.createChannelId(r, RackChannel.EMS_ADDRESS), - new UnsignedWordElement(r.offset + 0x000B)), // - m(this.createChannelId(r, RackChannel.EMS_BAUDRATE), - new UnsignedWordElement(r.offset + 0x000C)), // - new DummyRegisterElement(r.offset + 0x000D, r.offset + 0x000F), - m(this.createChannelId(r, RackChannel.PRE_CHARGE_CONTROL), - new UnsignedWordElement(r.offset + 0x0010)), // - new DummyRegisterElement(r.offset + 0x0011, r.offset + 0x0014), - m(this.createChannelId(r, RackChannel.SET_SUB_MASTER_ADDRESS), - new UnsignedWordElement(r.offset + 0x0015)) // - ), // - new FC3ReadRegistersTask(r.offset + 0x00F4, Priority.LOW, // - m(this.createChannelId(r, RackChannel.EMS_COMMUNICATION_TIMEOUT), - new UnsignedWordElement(r.offset + 0x00F4)) // - ), - - // Single Cluster Control Registers (running without Master BMS) - new FC6WriteRegisterTask(r.offset + 0x0010, // - m(this.createChannelId(r, RackChannel.PRE_CHARGE_CONTROL), - new UnsignedWordElement(r.offset + 0x0010)) // - ), // - new FC6WriteRegisterTask(r.offset + 0x00F4, // - m(this.createChannelId(r, RackChannel.EMS_COMMUNICATION_TIMEOUT), - new UnsignedWordElement(r.offset + 0x00F4)) // - ), // - new FC16WriteRegistersTask(r.offset + 0x000B, // - m(this.createChannelId(r, RackChannel.EMS_ADDRESS), - new UnsignedWordElement(r.offset + 0x000B)), // - m(this.createChannelId(r, RackChannel.EMS_BAUDRATE), - new UnsignedWordElement(r.offset + 0x000C)) // - ), // - - // Single Cluster Control Registers (General) - new FC6WriteRegisterTask(r.offset + 0x00CC, // - m(this.createChannelId(r, RackChannel.SYSTEM_TOTAL_CAPACITY), - new UnsignedWordElement(r.offset + 0x00CC)) // - ), // - new FC6WriteRegisterTask(r.offset + 0x0015, // - m(this.createChannelId(r, RackChannel.SET_SUB_MASTER_ADDRESS), - new UnsignedWordElement(r.offset + 0x0015)) // - ), // - new FC6WriteRegisterTask(r.offset + 0x00F3, // - m(this.createChannelId(r, RackChannel.VOLTAGE_LOW_PROTECTION), - new UnsignedWordElement(r.offset + 0x00F3)) // - ), // - new FC3ReadRegistersTask(r.offset + 0x00CC, Priority.LOW, // - m(this.createChannelId(r, RackChannel.SYSTEM_TOTAL_CAPACITY), - new UnsignedWordElement(r.offset + 0x00CC)) // - ), - - // Single Cluster Status Registers - new FC3ReadRegistersTask(r.offset + 0x100, Priority.HIGH, // - m(this.createChannelId(r, RackChannel.VOLTAGE), - new UnsignedWordElement(r.offset + 0x100), SCALE_FACTOR_2), - m(this.createChannelId(r, RackChannel.CURRENT), new SignedWordElement(r.offset + 0x101), - SCALE_FACTOR_2), - m(this.createChannelId(r, RackChannel.CHARGE_INDICATION), - new UnsignedWordElement(r.offset + 0x102)), - m(this.createChannelId(r, RackChannel.SOC), new UnsignedWordElement(r.offset + 0x103)), - m(this.createChannelId(r, RackChannel.SOH), new UnsignedWordElement(r.offset + 0x104)), - m(this.createChannelId(r, RackChannel.MAX_CELL_VOLTAGE_ID), - new UnsignedWordElement(r.offset + 0x105)), - m(this.createChannelId(r, RackChannel.MAX_CELL_VOLTAGE), - new UnsignedWordElement(r.offset + 0x106)), - m(this.createChannelId(r, RackChannel.MIN_CELL_VOLTAGE_ID), - new UnsignedWordElement(r.offset + 0x107)), - m(this.createChannelId(r, RackChannel.MIN_CELL_VOLTAGE), - new UnsignedWordElement(r.offset + 0x108)), - m(this.createChannelId(r, RackChannel.MAX_CELL_TEMPERATURE_ID), - new UnsignedWordElement(r.offset + 0x109)), - m(this.createChannelId(r, RackChannel.MAX_CELL_TEMPERATURE), - new SignedWordElement(r.offset + 0x10A), SCALE_FACTOR_MINUS_1), - m(this.createChannelId(r, RackChannel.MIN_CELL_TEMPERATURE_ID), - new UnsignedWordElement(r.offset + 0x10B)), - m(this.createChannelId(r, RackChannel.MIN_CELL_TEMPERATURE), - new SignedWordElement(r.offset + 0x10C), SCALE_FACTOR_MINUS_1), - m(this.createChannelId(r, RackChannel.AVERAGE_VOLTAGE), - new UnsignedWordElement(r.offset + 0x10D)), - m(this.createChannelId(r, RackChannel.SYSTEM_INSULATION), - new UnsignedWordElement(r.offset + 0x10E)), - m(this.createChannelId(r, RackChannel.SYSTEM_MAX_CHARGE_CURRENT), - new UnsignedWordElement(r.offset + 0x10F), SCALE_FACTOR_2), - m(this.createChannelId(r, RackChannel.SYSTEM_MAX_DISCHARGE_CURRENT), - new UnsignedWordElement(r.offset + 0x110), SCALE_FACTOR_2), - m(this.createChannelId(r, RackChannel.POSITIVE_INSULATION), - new UnsignedWordElement(r.offset + 0x111)), - m(this.createChannelId(r, RackChannel.NEGATIVE_INSULATION), - new UnsignedWordElement(r.offset + 0x112)), - m(this.createChannelId(r, RackChannel.CLUSTER_RUN_STATE), - new UnsignedWordElement(r.offset + 0x113)), - m(this.createChannelId(r, RackChannel.AVG_TEMPERATURE), - new SignedWordElement(r.offset + 0x114))), - new FC3ReadRegistersTask(r.offset + 0x18b, Priority.LOW, - m(this.createChannelId(r, RackChannel.PROJECT_ID), - new UnsignedWordElement(r.offset + 0x18b)), - m(this.createChannelId(r, RackChannel.VERSION_MAJOR), - new UnsignedWordElement(r.offset + 0x18c)), - m(this.createChannelId(r, RackChannel.VERSION_SUB), - new UnsignedWordElement(r.offset + 0x18d)), - m(this.createChannelId(r, RackChannel.VERSION_MODIFY), - new UnsignedWordElement(r.offset + 0x18e))), - - // System Warning/Shut Down Status Registers - new FC3ReadRegistersTask(r.offset + 0x140, Priority.LOW, - // Level 2 Alarm: BMS Self-protect, main contactor shut down - m(new BitsWordElement(r.offset + 0x140, this) // - .bit(0, this.createChannelId(r, RackChannel.LEVEL2_CELL_VOLTAGE_HIGH)) // - .bit(1, this.createChannelId(r, RackChannel.LEVEL2_TOTAL_VOLTAGE_HIGH)) // - .bit(2, this.createChannelId(r, RackChannel.LEVEL2_CHARGE_CURRENT_HIGH)) // - .bit(3, this.createChannelId(r, RackChannel.LEVEL2_CELL_VOLTAGE_LOW)) // - .bit(4, this.createChannelId(r, RackChannel.LEVEL2_TOTAL_VOLTAGE_LOW)) // - .bit(5, this.createChannelId(r, RackChannel.LEVEL2_DISCHARGE_CURRENT_HIGH)) // - .bit(6, this.createChannelId(r, RackChannel.LEVEL2_CHARGE_TEMP_HIGH)) // - .bit(7, this.createChannelId(r, RackChannel.LEVEL2_CHARGE_TEMP_LOW)) // - // 8 -> Reserved - // 9 -> Reserved - .bit(10, this.createChannelId(r, RackChannel.LEVEL2_POWER_POLE_TEMP_HIGH)) // - // 11 -> Reserved - .bit(12, this.createChannelId(r, RackChannel.LEVEL2_INSULATION_VALUE)) // - // 13 -> Reserved - .bit(14, this.createChannelId(r, RackChannel.LEVEL2_DISCHARGE_TEMP_HIGH)) // - .bit(15, this.createChannelId(r, RackChannel.LEVEL2_DISCHARGE_TEMP_LOW)) // - ), - // Level 1 Alarm: EMS Control to stop charge, discharge, charge&discharge - m(new BitsWordElement(r.offset + 0x141, this) // - .bit(1, this.createChannelId(r, RackChannel.LEVEL1_TOTAL_VOLTAGE_HIGH)) // - .bit(2, this.createChannelId(r, RackChannel.LEVEL1_CHARGE_CURRENT_HIGH)) // - .bit(4, this.createChannelId(r, RackChannel.LEVEL1_TOTAL_VOLTAGE_LOW)) // - .bit(5, this.createChannelId(r, RackChannel.LEVEL1_DISCHARGE_CURRENT_HIGH)) // - .bit(6, this.createChannelId(r, RackChannel.LEVEL1_CHARGE_TEMP_HIGH)) // - .bit(7, this.createChannelId(r, RackChannel.LEVEL1_CHARGE_TEMP_LOW)) // - .bit(8, this.createChannelId(r, RackChannel.LEVEL1_SOC_LOW)) // - .bit(9, this.createChannelId(r, RackChannel.LEVEL1_TEMP_DIFF_TOO_BIG)) // - .bit(10, this.createChannelId(r, RackChannel.LEVEL1_POWER_POLE_TEMP_HIGH)) // - .bit(11, this.createChannelId(r, RackChannel.LEVEL1_CELL_VOLTAGE_DIFF_TOO_BIG)) // - .bit(12, this.createChannelId(r, RackChannel.LEVEL1_INSULATION_VALUE)) // - .bit(13, this.createChannelId(r, RackChannel.LEVEL1_TOTAL_VOLTAGE_DIFF_TOO_BIG)) // - .bit(14, this.createChannelId(r, RackChannel.LEVEL1_DISCHARGE_TEMP_HIGH)) // - .bit(15, this.createChannelId(r, RackChannel.LEVEL1_DISCHARGE_TEMP_LOW)) // - ), - // Pre-Alarm: Temperature Alarm will active current limication - m(new BitsWordElement(r.offset + 0x142, this) // - .bit(2, this.createChannelId(r, RackChannel.PRE_ALARM_CHARGE_CURRENT_HIGH)) // - .bit(4, this.createChannelId(r, RackChannel.PRE_ALARM_TOTAL_VOLTAGE_LOW)) // - .bit(5, this.createChannelId(r, RackChannel.PRE_ALARM_DISCHARGE_CURRENT_HIGH)) // - .bit(6, this.createChannelId(r, RackChannel.PRE_ALARM_CHARGE_TEMP_HIGH)) // - .bit(7, this.createChannelId(r, RackChannel.PRE_ALARM_CHARGE_TEMP_LOW)) // - .bit(10, this.createChannelId(r, RackChannel.PRE_ALARM_POWER_POLE_HIGH))// - .bit(11, this.createChannelId(r, - RackChannel.PRE_ALARM_CELL_VOLTAGE_DIFF_TOO_BIG)) // - .bit(12, this.createChannelId(r, RackChannel.PRE_ALARM_INSULATION_FAIL)) // - .bit(13, this.createChannelId(r, - RackChannel.PRE_ALARM_TOTAL_VOLTAGE_DIFF_TOO_BIG)) // - .bit(14, this.createChannelId(r, RackChannel.PRE_ALARM_DISCHARGE_TEMP_HIGH)) // - .bit(15, this.createChannelId(r, RackChannel.PRE_ALARM_DISCHARGE_TEMP_LOW)) // - ) // - ), - // Other Alarm Info - new FC3ReadRegistersTask(r.offset + 0x1A5, Priority.LOW, // - m(new BitsWordElement(r.offset + 0x1A5, this) // - .bit(0, this.createChannelId(r, RackChannel.ALARM_COMMUNICATION_TO_MASTER_BMS)) // - .bit(1, this.createChannelId(r, RackChannel.ALARM_COMMUNICATION_TO_SLAVE_BMS)) // - .bit(2, this.createChannelId(r, - RackChannel.ALARM_COMMUNICATION_SLAVE_BMS_TO_TEMP_SENSORS)) // - .bit(3, this.createChannelId(r, RackChannel.ALARM_SLAVE_BMS_HARDWARE)) // - )), - // Slave BMS Fault Message Registers - new FC3ReadRegistersTask(r.offset + 0x185, Priority.LOW, // - m(new BitsWordElement(r.offset + 0x185, this) // - .bit(0, this.createChannelId(r, RackChannel.SLAVE_BMS_VOLTAGE_SENSOR_CABLES)) // - .bit(1, this.createChannelId(r, RackChannel.SLAVE_BMS_POWER_CABLE)) // - .bit(2, this.createChannelId(r, RackChannel.SLAVE_BMS_LTC6803)) // - .bit(3, this.createChannelId(r, RackChannel.SLAVE_BMS_VOLTAGE_SENSORS)) // - .bit(4, this.createChannelId(r, RackChannel.SLAVE_BMS_TEMP_SENSOR_CABLES)) // - .bit(5, this.createChannelId(r, RackChannel.SLAVE_BMS_TEMP_SENSORS)) // - .bit(6, this.createChannelId(r, RackChannel.SLAVE_BMS_POWER_POLE_TEMP_SENSOR)) // - .bit(7, this.createChannelId(r, RackChannel.SLAVE_BMS_TEMP_BOARD_COM)) // - .bit(8, this.createChannelId(r, RackChannel.SLAVE_BMS_BALANCE_MODULE)) // - .bit(9, this.createChannelId(r, RackChannel.SLAVE_BMS_TEMP_SENSORS2)) // - .bit(10, this.createChannelId(r, RackChannel.SLAVE_BMS_INTERNAL_COM)) // - .bit(11, this.createChannelId(r, RackChannel.SLAVE_BMS_EEPROM)) // - .bit(12, this.createChannelId(r, RackChannel.SLAVE_BMS_INIT)) // - )) // - ); - } catch (OpenemsException e) { - this.logError(this.log, "Error while creating modbus tasks: " + e.getMessage()); - e.printStackTrace(); - } // + this.getModbusProtocol().addTasks(// + + new FC3ReadRegistersTask(r.offset + 0x000B, Priority.LOW, // + m(this.createChannelId(r, RackChannel.EMS_ADDRESS), + new UnsignedWordElement(r.offset + 0x000B)), // + m(this.createChannelId(r, RackChannel.EMS_BAUDRATE), + new UnsignedWordElement(r.offset + 0x000C)), // + new DummyRegisterElement(r.offset + 0x000D, r.offset + 0x000F), + m(this.createChannelId(r, RackChannel.PRE_CHARGE_CONTROL), + new UnsignedWordElement(r.offset + 0x0010)), // + new DummyRegisterElement(r.offset + 0x0011, r.offset + 0x0014), + m(this.createChannelId(r, RackChannel.SET_SUB_MASTER_ADDRESS), + new UnsignedWordElement(r.offset + 0x0015)) // + ), // + new FC3ReadRegistersTask(r.offset + 0x00F4, Priority.LOW, // + m(this.createChannelId(r, RackChannel.EMS_COMMUNICATION_TIMEOUT), + new UnsignedWordElement(r.offset + 0x00F4)) // + ), + + // Single Cluster Control Registers (running without Master BMS) + new FC6WriteRegisterTask(r.offset + 0x0010, // + m(this.createChannelId(r, RackChannel.PRE_CHARGE_CONTROL), + new UnsignedWordElement(r.offset + 0x0010)) // + ), // + new FC6WriteRegisterTask(r.offset + 0x00F4, // + m(this.createChannelId(r, RackChannel.EMS_COMMUNICATION_TIMEOUT), + new UnsignedWordElement(r.offset + 0x00F4)) // + ), // + new FC16WriteRegistersTask(r.offset + 0x000B, // + m(this.createChannelId(r, RackChannel.EMS_ADDRESS), + new UnsignedWordElement(r.offset + 0x000B)), // + m(this.createChannelId(r, RackChannel.EMS_BAUDRATE), + new UnsignedWordElement(r.offset + 0x000C)) // + ), // + + // Single Cluster Control Registers (General) + new FC6WriteRegisterTask(r.offset + 0x00CC, // + m(this.createChannelId(r, RackChannel.SYSTEM_TOTAL_CAPACITY), + new UnsignedWordElement(r.offset + 0x00CC)) // + ), // + new FC6WriteRegisterTask(r.offset + 0x0015, // + m(this.createChannelId(r, RackChannel.SET_SUB_MASTER_ADDRESS), + new UnsignedWordElement(r.offset + 0x0015)) // + ), // + new FC6WriteRegisterTask(r.offset + 0x00F3, // + m(this.createChannelId(r, RackChannel.VOLTAGE_LOW_PROTECTION), + new UnsignedWordElement(r.offset + 0x00F3)) // + ), // + new FC3ReadRegistersTask(r.offset + 0x00CC, Priority.LOW, // + m(this.createChannelId(r, RackChannel.SYSTEM_TOTAL_CAPACITY), + new UnsignedWordElement(r.offset + 0x00CC)) // + ), + + // Single Cluster Status Registers + new FC3ReadRegistersTask(r.offset + 0x100, Priority.HIGH, // + m(this.createChannelId(r, RackChannel.VOLTAGE), new UnsignedWordElement(r.offset + 0x100), + SCALE_FACTOR_2), + m(this.createChannelId(r, RackChannel.CURRENT), new SignedWordElement(r.offset + 0x101), + SCALE_FACTOR_2), + m(this.createChannelId(r, RackChannel.CHARGE_INDICATION), + new UnsignedWordElement(r.offset + 0x102)), + m(this.createChannelId(r, RackChannel.SOC), new UnsignedWordElement(r.offset + 0x103)), + m(this.createChannelId(r, RackChannel.SOH), new UnsignedWordElement(r.offset + 0x104)), + m(this.createChannelId(r, RackChannel.MAX_CELL_VOLTAGE_ID), + new UnsignedWordElement(r.offset + 0x105)), + m(this.createChannelId(r, RackChannel.MAX_CELL_VOLTAGE), + new UnsignedWordElement(r.offset + 0x106)), + m(this.createChannelId(r, RackChannel.MIN_CELL_VOLTAGE_ID), + new UnsignedWordElement(r.offset + 0x107)), + m(this.createChannelId(r, RackChannel.MIN_CELL_VOLTAGE), + new UnsignedWordElement(r.offset + 0x108)), + m(this.createChannelId(r, RackChannel.MAX_CELL_TEMPERATURE_ID), + new UnsignedWordElement(r.offset + 0x109)), + m(this.createChannelId(r, RackChannel.MAX_CELL_TEMPERATURE), + new SignedWordElement(r.offset + 0x10A), SCALE_FACTOR_MINUS_1), + m(this.createChannelId(r, RackChannel.MIN_CELL_TEMPERATURE_ID), + new UnsignedWordElement(r.offset + 0x10B)), + m(this.createChannelId(r, RackChannel.MIN_CELL_TEMPERATURE), + new SignedWordElement(r.offset + 0x10C), SCALE_FACTOR_MINUS_1), + m(this.createChannelId(r, RackChannel.AVERAGE_VOLTAGE), + new UnsignedWordElement(r.offset + 0x10D)), + m(this.createChannelId(r, RackChannel.SYSTEM_INSULATION), + new UnsignedWordElement(r.offset + 0x10E)), + m(this.createChannelId(r, RackChannel.SYSTEM_MAX_CHARGE_CURRENT), + new UnsignedWordElement(r.offset + 0x10F), SCALE_FACTOR_2), + m(this.createChannelId(r, RackChannel.SYSTEM_MAX_DISCHARGE_CURRENT), + new UnsignedWordElement(r.offset + 0x110), SCALE_FACTOR_2), + m(this.createChannelId(r, RackChannel.POSITIVE_INSULATION), + new UnsignedWordElement(r.offset + 0x111)), + m(this.createChannelId(r, RackChannel.NEGATIVE_INSULATION), + new UnsignedWordElement(r.offset + 0x112)), + m(this.createChannelId(r, RackChannel.CLUSTER_RUN_STATE), + new UnsignedWordElement(r.offset + 0x113)), + m(this.createChannelId(r, RackChannel.AVG_TEMPERATURE), + new SignedWordElement(r.offset + 0x114))), + new FC3ReadRegistersTask(r.offset + 0x18b, Priority.LOW, + m(this.createChannelId(r, RackChannel.PROJECT_ID), + new UnsignedWordElement(r.offset + 0x18b)), + m(this.createChannelId(r, RackChannel.VERSION_MAJOR), + new UnsignedWordElement(r.offset + 0x18c)), + m(this.createChannelId(r, RackChannel.VERSION_SUB), + new UnsignedWordElement(r.offset + 0x18d)), + m(this.createChannelId(r, RackChannel.VERSION_MODIFY), + new UnsignedWordElement(r.offset + 0x18e))), + + // System Warning/Shut Down Status Registers + new FC3ReadRegistersTask(r.offset + 0x140, Priority.LOW, + // Level 2 Alarm: BMS Self-protect, main contactor shut down + m(new BitsWordElement(r.offset + 0x140, this) // + .bit(0, this.createChannelId(r, RackChannel.LEVEL2_CELL_VOLTAGE_HIGH)) // + .bit(1, this.createChannelId(r, RackChannel.LEVEL2_TOTAL_VOLTAGE_HIGH)) // + .bit(2, this.createChannelId(r, RackChannel.LEVEL2_CHARGE_CURRENT_HIGH)) // + .bit(3, this.createChannelId(r, RackChannel.LEVEL2_CELL_VOLTAGE_LOW)) // + .bit(4, this.createChannelId(r, RackChannel.LEVEL2_TOTAL_VOLTAGE_LOW)) // + .bit(5, this.createChannelId(r, RackChannel.LEVEL2_DISCHARGE_CURRENT_HIGH)) // + .bit(6, this.createChannelId(r, RackChannel.LEVEL2_CHARGE_TEMP_HIGH)) // + .bit(7, this.createChannelId(r, RackChannel.LEVEL2_CHARGE_TEMP_LOW)) // + // 8 -> Reserved + // 9 -> Reserved + .bit(10, this.createChannelId(r, RackChannel.LEVEL2_POWER_POLE_TEMP_HIGH)) // + // 11 -> Reserved + .bit(12, this.createChannelId(r, RackChannel.LEVEL2_INSULATION_VALUE)) // + // 13 -> Reserved + .bit(14, this.createChannelId(r, RackChannel.LEVEL2_DISCHARGE_TEMP_HIGH)) // + .bit(15, this.createChannelId(r, RackChannel.LEVEL2_DISCHARGE_TEMP_LOW)) // + ), + // Level 1 Alarm: EMS Control to stop charge, discharge, charge&discharge + m(new BitsWordElement(r.offset + 0x141, this) // + .bit(1, this.createChannelId(r, RackChannel.LEVEL1_TOTAL_VOLTAGE_HIGH)) // + .bit(2, this.createChannelId(r, RackChannel.LEVEL1_CHARGE_CURRENT_HIGH)) // + .bit(4, this.createChannelId(r, RackChannel.LEVEL1_TOTAL_VOLTAGE_LOW)) // + .bit(5, this.createChannelId(r, RackChannel.LEVEL1_DISCHARGE_CURRENT_HIGH)) // + .bit(6, this.createChannelId(r, RackChannel.LEVEL1_CHARGE_TEMP_HIGH)) // + .bit(7, this.createChannelId(r, RackChannel.LEVEL1_CHARGE_TEMP_LOW)) // + .bit(8, this.createChannelId(r, RackChannel.LEVEL1_SOC_LOW)) // + .bit(9, this.createChannelId(r, RackChannel.LEVEL1_TEMP_DIFF_TOO_BIG)) // + .bit(10, this.createChannelId(r, RackChannel.LEVEL1_POWER_POLE_TEMP_HIGH)) // + .bit(11, this.createChannelId(r, RackChannel.LEVEL1_CELL_VOLTAGE_DIFF_TOO_BIG)) // + .bit(12, this.createChannelId(r, RackChannel.LEVEL1_INSULATION_VALUE)) // + .bit(13, this.createChannelId(r, RackChannel.LEVEL1_TOTAL_VOLTAGE_DIFF_TOO_BIG)) // + .bit(14, this.createChannelId(r, RackChannel.LEVEL1_DISCHARGE_TEMP_HIGH)) // + .bit(15, this.createChannelId(r, RackChannel.LEVEL1_DISCHARGE_TEMP_LOW)) // + ), + // Pre-Alarm: Temperature Alarm will active current limication + m(new BitsWordElement(r.offset + 0x142, this) // + .bit(2, this.createChannelId(r, RackChannel.PRE_ALARM_CHARGE_CURRENT_HIGH)) // + .bit(4, this.createChannelId(r, RackChannel.PRE_ALARM_TOTAL_VOLTAGE_LOW)) // + .bit(5, this.createChannelId(r, RackChannel.PRE_ALARM_DISCHARGE_CURRENT_HIGH)) // + .bit(6, this.createChannelId(r, RackChannel.PRE_ALARM_CHARGE_TEMP_HIGH)) // + .bit(7, this.createChannelId(r, RackChannel.PRE_ALARM_CHARGE_TEMP_LOW)) // + .bit(10, this.createChannelId(r, RackChannel.PRE_ALARM_POWER_POLE_HIGH))// + .bit(11, this.createChannelId(r, RackChannel.PRE_ALARM_CELL_VOLTAGE_DIFF_TOO_BIG)) // + .bit(12, this.createChannelId(r, RackChannel.PRE_ALARM_INSULATION_FAIL)) // + .bit(13, this.createChannelId(r, RackChannel.PRE_ALARM_TOTAL_VOLTAGE_DIFF_TOO_BIG)) // + .bit(14, this.createChannelId(r, RackChannel.PRE_ALARM_DISCHARGE_TEMP_HIGH)) // + .bit(15, this.createChannelId(r, RackChannel.PRE_ALARM_DISCHARGE_TEMP_LOW)) // + ) // + ), + // Other Alarm Info + new FC3ReadRegistersTask(r.offset + 0x1A5, Priority.LOW, // + m(new BitsWordElement(r.offset + 0x1A5, this) // + .bit(0, this.createChannelId(r, RackChannel.ALARM_COMMUNICATION_TO_MASTER_BMS)) // + .bit(1, this.createChannelId(r, RackChannel.ALARM_COMMUNICATION_TO_SLAVE_BMS)) // + .bit(2, this.createChannelId(r, + RackChannel.ALARM_COMMUNICATION_SLAVE_BMS_TO_TEMP_SENSORS)) // + .bit(3, this.createChannelId(r, RackChannel.ALARM_SLAVE_BMS_HARDWARE)) // + )), + // Slave BMS Fault Message Registers + new FC3ReadRegistersTask(r.offset + 0x185, Priority.LOW, // + m(new BitsWordElement(r.offset + 0x185, this) // + .bit(0, this.createChannelId(r, RackChannel.SLAVE_BMS_VOLTAGE_SENSOR_CABLES)) // + .bit(1, this.createChannelId(r, RackChannel.SLAVE_BMS_POWER_CABLE)) // + .bit(2, this.createChannelId(r, RackChannel.SLAVE_BMS_LTC6803)) // + .bit(3, this.createChannelId(r, RackChannel.SLAVE_BMS_VOLTAGE_SENSORS)) // + .bit(4, this.createChannelId(r, RackChannel.SLAVE_BMS_TEMP_SENSOR_CABLES)) // + .bit(5, this.createChannelId(r, RackChannel.SLAVE_BMS_TEMP_SENSORS)) // + .bit(6, this.createChannelId(r, RackChannel.SLAVE_BMS_POWER_POLE_TEMP_SENSOR)) // + .bit(7, this.createChannelId(r, RackChannel.SLAVE_BMS_TEMP_BOARD_COM)) // + .bit(8, this.createChannelId(r, RackChannel.SLAVE_BMS_BALANCE_MODULE)) // + .bit(9, this.createChannelId(r, RackChannel.SLAVE_BMS_TEMP_SENSORS2)) // + .bit(10, this.createChannelId(r, RackChannel.SLAVE_BMS_INTERNAL_COM)) // + .bit(11, this.createChannelId(r, RackChannel.SLAVE_BMS_EEPROM)) // + .bit(12, this.createChannelId(r, RackChannel.SLAVE_BMS_INIT)) // + )) // + ); Consumer addCellChannels = type -> { for (var i = 0; i < numberOfModules; i++) { var elements = new ModbusElement[type.getSensorsPerModule()]; @@ -389,14 +385,9 @@ private void updateRackChannels(Integer numberOfModules, TreeSet racks) th elements[j] = m(channelId, new UnsignedWordElement(r.offset + type.getOffset() + sensorIndex)); } // Add a Modbus read task for this module - try { - this.getModbusProtocol().addTasks(// - new FC3ReadRegistersTask(r.offset + type.getOffset() + i * type.getSensorsPerModule(), - Priority.LOW, elements)); - } catch (OpenemsException e) { - this.logError(this.log, "Error while creating modbus tasks: " + e.getMessage()); - e.printStackTrace(); - } + this.getModbusProtocol().addTasks(// + new FC3ReadRegistersTask(r.offset + type.getOffset() + i * type.getSensorsPerModule(), + Priority.LOW, elements)); } }; addCellChannels.accept(CellChannelFactory.Type.VOLTAGE_CLUSTER); @@ -657,38 +648,24 @@ private void calculateCapacity(int numberOfTowers, int numberOfModules) { * @throws OpenemsException on error */ private CompletableFuture getNumberOfModules() { - final var result = new CompletableFuture(); - - try { - ModbusUtils - .readELementOnce(this.getModbusProtocol(), - new UnsignedWordElement(0x20C1 /* No of modules for 1st tower */), true) - .thenAccept(numberOfModules -> { - if (numberOfModules == null) { - return; - } - result.complete(numberOfModules); - }); - } catch (OpenemsException e) { - result.completeExceptionally(e); - } - - return result; + return readElementOnce(this.getModbusProtocol(), ModbusUtils::retryOnNull, + new UnsignedWordElement(0x20C1 /* No of modules for 1st tower */)); } /** * Recursively reads the 'No of modules' register of each tower. Eventually * completes the {@link CompletableFuture}. * + * @param retryPredicate yield true to retry reading values; false + * otherwise. Parameters are the {@link ExecuteState} + * of the entire task and the individual element + * value * @param result the {@link CompletableFuture} * @param totalNumberOfTowers the recursively incremented total number of towers * @param addresses Queue with the remaining 'No of modules' registers - * @param tryAgainOnError if true, tries to read till it receives a value; - * if false, stops after first try and possibly - * return null */ - private void checkNumberOfTowers(CompletableFuture result, int totalNumberOfTowers, - final Queue addresses, boolean tryAgainOnError) { + private void checkNumberOfTowers(BiPredicate retryPredicate, + CompletableFuture result, int totalNumberOfTowers, final Queue addresses) { final var address = addresses.poll(); if (address == null) { @@ -697,28 +674,18 @@ private void checkNumberOfTowers(CompletableFuture result, int totalNum return; } - try { - // Read next address in Queue - ModbusUtils.readELementOnce(this.getModbusProtocol(), new UnsignedWordElement(address), tryAgainOnError) - .thenAccept(numberOfModules -> { - if (numberOfModules == null) { - if (tryAgainOnError) { - // Try again - return; - } - // Read error -> this tower does not exist. Stop here. - result.complete(totalNumberOfTowers); - return; - } - - // Read successful -> try to read next tower - this.checkNumberOfTowers(result, totalNumberOfTowers + 1, addresses, false); - }); - } catch (OpenemsException e) { - e.printStackTrace(); - result.completeExceptionally(e); - return; - } + // Read next address in Queue + readElementOnce(this.getModbusProtocol(), retryPredicate, new UnsignedWordElement(address)) + .thenAccept(numberOfModules -> { + if (numberOfModules == null) { + // Read error -> this tower does not exist. Stop here. + result.complete(totalNumberOfTowers); + return; + } + + // Read successful -> try to read next tower + this.checkNumberOfTowers(ModbusUtils::doNotRetry, result, totalNumberOfTowers + 1, addresses); + }); } private CompletableFuture getNumberOfTowers() throws OpenemsException { @@ -731,7 +698,7 @@ private CompletableFuture getNumberOfTowers() throws OpenemsException { addresses.add(0x50C1 /* No of modules for 4th tower */); addresses.add(0x60C1 /* No of modules for 5th tower */); - this.checkNumberOfTowers(result, 0, addresses, true); + this.checkNumberOfTowers(ModbusUtils::retryOnNull, result, 0, addresses); return result; } @@ -786,7 +753,7 @@ public String debugLog() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, /* * BMS Control Registers diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionAImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionAImpl.java index 125aba6961e..92bf44d0a43 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionAImpl.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versiona/BatterySoltaroSingleRackVersionAImpl.java @@ -435,7 +435,7 @@ private void stopSystem() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC6WriteRegisterTask(0x2010, // m(BatterySoltaroSingleRackVersionA.ChannelId.BMS_CONTACTOR_CONTROL, diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImpl.java index 28a0e5f4268..2bc1e0c7db0 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImpl.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionb/BatterySoltaroSingleRackVersionBImpl.java @@ -4,6 +4,7 @@ import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_1; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -230,7 +231,7 @@ public String debugLog() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var protocol = new ModbusProtocol(this, // // Main switch @@ -914,22 +915,9 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { * Gets the Number of Modules. * * @return the Number of Modules as a {@link CompletableFuture}. - * @throws OpenemsException on error */ private CompletableFuture getNumberOfModules() { - final var result = new CompletableFuture(); - try { - ModbusUtils.readELementOnce(this.getModbusProtocol(), new UnsignedWordElement(0x20C1), true) - .thenAccept(numberOfModules -> { - if (numberOfModules == null) { - return; - } - result.complete(numberOfModules); - }); - } catch (OpenemsException e) { - result.completeExceptionally(e); - } - return result; + return readElementOnce(this.getModbusProtocol(), ModbusUtils::retryOnNull, new UnsignedWordElement(0x20C1)); } /** @@ -955,41 +943,35 @@ private void calculateCapacity(Integer numberOfModules) { * @param numberOfModules the number of battery modules */ private void createDynamicChannels(int numberOfModules) { - try { - for (var i = 0; i < numberOfModules; i++) { - var ameVolt = new ModbusElement[SENSORS_PER_MODULE]; - var ameTemp = new ModbusElement[SENSORS_PER_MODULE]; - for (var j = 0; j < SENSORS_PER_MODULE; j++) { - var sensor = i * SENSORS_PER_MODULE + j; - { - // Create Voltage Channel - var channelId = new ChannelIdImpl( - "CLUSTER_1_BATTERY_" + String.format("%03d", sensor) + "_VOLTAGE", - Doc.of(OpenemsType.INTEGER).unit(Unit.MILLIVOLT)); - this.addChannel(channelId); - // Create Modbus-Mapping for Voltages - var uwe = new UnsignedWordElement(VOLTAGE_ADDRESS_OFFSET + sensor); - ameVolt[j] = m(channelId, uwe); - } - { - // Create Temperature Channel - var channelId = new ChannelIdImpl( - "CLUSTER_1_BATTERY_" + String.format("%03d", sensor) + "_TEMPERATURE", - Doc.of(OpenemsType.INTEGER).unit(Unit.DEZIDEGREE_CELSIUS)); - this.addChannel(channelId); - // Create Modbus-Mapping for Temperatures - var uwe = new UnsignedWordElement(TEMPERATURE_ADDRESS_OFFSET + sensor); - ameTemp[j] = m(channelId, uwe); - } + for (var i = 0; i < numberOfModules; i++) { + var ameVolt = new ModbusElement[SENSORS_PER_MODULE]; + var ameTemp = new ModbusElement[SENSORS_PER_MODULE]; + for (var j = 0; j < SENSORS_PER_MODULE; j++) { + var sensor = i * SENSORS_PER_MODULE + j; + { + // Create Voltage Channel + var channelId = new ChannelIdImpl("CLUSTER_1_BATTERY_" + String.format("%03d", sensor) + "_VOLTAGE", + Doc.of(OpenemsType.INTEGER).unit(Unit.MILLIVOLT)); + this.addChannel(channelId); + // Create Modbus-Mapping for Voltages + var uwe = new UnsignedWordElement(VOLTAGE_ADDRESS_OFFSET + sensor); + ameVolt[j] = m(channelId, uwe); + } + { + // Create Temperature Channel + var channelId = new ChannelIdImpl( + "CLUSTER_1_BATTERY_" + String.format("%03d", sensor) + "_TEMPERATURE", + Doc.of(OpenemsType.INTEGER).unit(Unit.DEZIDEGREE_CELSIUS)); + this.addChannel(channelId); + // Create Modbus-Mapping for Temperatures + var uwe = new UnsignedWordElement(TEMPERATURE_ADDRESS_OFFSET + sensor); + ameTemp[j] = m(channelId, uwe); } - this.getModbusProtocol().addTasks(// - new FC3ReadRegistersTask(VOLTAGE_ADDRESS_OFFSET + i * SENSORS_PER_MODULE, Priority.LOW, - ameVolt), // - new FC3ReadRegistersTask(TEMPERATURE_ADDRESS_OFFSET + i * SENSORS_PER_MODULE, Priority.LOW, - ameTemp)); } - } catch (OpenemsException e) { - e.printStackTrace(); + this.getModbusProtocol().addTasks(// + new FC3ReadRegistersTask(VOLTAGE_ADDRESS_OFFSET + i * SENSORS_PER_MODULE, Priority.LOW, ameVolt), // + new FC3ReadRegistersTask(TEMPERATURE_ADDRESS_OFFSET + i * SENSORS_PER_MODULE, Priority.LOW, + ameTemp)); } } } diff --git a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImpl.java b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImpl.java index 4d1246d3c20..69feb15eeaa 100644 --- a/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImpl.java +++ b/io.openems.edge.battery.soltaro/src/io/openems/edge/battery/soltaro/single/versionc/BatterySoltaroSingleRackVersionCImpl.java @@ -3,6 +3,7 @@ import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.DIRECT_1_TO_1; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @@ -27,7 +28,6 @@ import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.battery.api.Battery; import io.openems.edge.battery.protection.BatteryProtection; import io.openems.edge.battery.soltaro.common.batteryprotection.BatteryProtectionDefinitionSoltaro3500Wh; @@ -152,22 +152,9 @@ private void calculateCapacity(Integer numberOfModules) { * Gets the Number of Modules. * * @return the Number of Modules as a {@link CompletableFuture}. - * @throws OpenemsException on error */ private CompletableFuture getNumberOfModules() { - final var result = new CompletableFuture(); - try { - ModbusUtils.readELementOnce(this.getModbusProtocol(), new UnsignedWordElement(0x20C1), true) - .thenAccept(numberOfModules -> { - if (numberOfModules == null) { - return; - } - result.complete(numberOfModules); - }); - } catch (OpenemsException e) { - result.completeExceptionally(e); - } - return result; + return readElementOnce(this.getModbusProtocol(), ModbusUtils::retryOnNull, new UnsignedWordElement(0x20C1)); } @Override @@ -219,7 +206,7 @@ public String debugLog() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var protocol = new ModbusProtocol(this, // new FC6WriteRegisterTask(0x2004, // m(BatterySoltaroSingleRackVersionC.ChannelId.SYSTEM_RESET, new UnsignedWordElement(0x2004)) // @@ -731,14 +718,8 @@ void createCellVoltageAndTemperatureChannels(int numberOfModules) { } // Add a Modbus read task for this module var startAddress = type.getOffset() + i * type.getSensorsPerModule(); - try { - this.getModbusProtocol().addTask(// - new FC3ReadRegistersTask(startAddress, Priority.LOW, elements)); - } catch (OpenemsException e) { - this.logWarn(this.log, "Error while adding Modbus task for slave [" + i + "] starting at [" - + startAddress + "]: " + e.getMessage()); - e.printStackTrace(); - } + this.getModbusProtocol().addTask(// + new FC3ReadRegistersTask(startAddress, Priority.LOW, elements)); } }; addCellChannels.accept(CellChannelFactory.Type.VOLTAGE_SINGLE); diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/doc/statemachine.md b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/doc/statemachine.md index 1e8ee842279..2ba632a58d8 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/doc/statemachine.md +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/doc/statemachine.md @@ -2,26 +2,28 @@ ```mermaid graph LR -Undefined -->|target START| GoRunning +Undefined --> |hasFault| Error +Undefined --> |isStarted or Grid Connected| Running +Undefined --> |isStopped or Off, Standby, Pre-charge| Stopped +Undefined --> |else| Go_Stopped -GoRunning -->|not timeout| GoRunning -GoRunning -->|isRunning| Running -GoRunning -->|timeout| Undefined +Go_Stopped --> |Off or Standby or Pre-charge| Stopped +Go_Stopped --> |hasFault or timeout| Error +Go_Stopped --> |try for 240 second| Go_Stopped -Running -->|isRunning && everythingOk| Running -Running -->|otherwise| Undefined +Stopped --> |targetStart| Go_Running +Stopped --> |hasFault| Error -Undefined -->|target STOP| GoStopped -GoStopped -->|isStopped| Stopped -GoStopped -->|not timeout| GoStopped -GoStopped -->|timeout| Undefined +Go_Running --> |targetStop| Go_Stopped +Go_Running --> |hasFault or timeout| Error +Go_Running --> |try for 240 second| Go_Running +Go_Running --> |Grid Connected or Throttled| Running -Stopped -->|isStopped && everythingOk| Stopped -Stopped -->|otherwise| Undefined +Running --> |hasFault| Error +Running --> |targetStop| Go_Stopped -Undefined -->|hasFault| ErrorHandling -ErrorHandling -->|not timeout| ErrorHandling -ErrorHandling -->|eventually| Undefined +Error --> |!hasFault| Stopped +Error --> |hasFault| Error ``` View using Mermaid, e.g. https://mermaid-js.github.io/mermaid-live-editor \ No newline at end of file diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsave.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsave.java index 1e83c03b677..193b3dbd1d7 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsave.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsave.java @@ -8,6 +8,7 @@ import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201CurrentState; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201RequestedState; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine.StateMachine.State; +import io.openems.edge.bridge.modbus.api.ModbusComponent; import io.openems.edge.bridge.modbus.sunspec.SunSpecPoint; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; @@ -18,8 +19,8 @@ import io.openems.edge.common.startstop.StartStop; import io.openems.edge.common.startstop.StartStoppable; -public interface BatteryInverterKacoBlueplanetGridsave - extends ManagedSymmetricBatteryInverter, SymmetricBatteryInverter, OpenemsComponent, StartStoppable { +public interface BatteryInverterKacoBlueplanetGridsave extends ManagedSymmetricBatteryInverter, + SymmetricBatteryInverter, ModbusComponent, OpenemsComponent, StartStoppable { /** * Sets the KACO watchdog timeout to 60 seconds. @@ -31,28 +32,19 @@ public interface BatteryInverterKacoBlueplanetGridsave */ public static final int WATCHDOG_TRIGGER_SECONDS = 10; - /** - * Retry set-command after x Seconds, e.g. for starting battery or - * battery-inverter. - */ - public static int RETRY_COMMAND_SECONDS = 30; - - /** - * Retry x attempts for set-command. - */ - public static int RETRY_COMMAND_MAX_ATTEMPTS = 30; - public enum ChannelId implements io.openems.edge.common.channel.ChannelId { STATE_MACHINE(Doc.of(State.values()) // .text("Current State of State-Machine")), // RUN_FAILED(Doc.of(Level.FAULT) // .text("Running the Logic failed")), // - MAX_START_ATTEMPTS(Doc.of(Level.FAULT) // - .text("The maximum number of start attempts failed")), // - MAX_STOP_ATTEMPTS(Doc.of(Level.FAULT) // - .text("The maximum number of stop attempts failed")), // + MAX_START_TIMEOUT(Doc.of(Level.FAULT) // + .text("Max start time is exceeded")), // + MAX_STOP_TIMEOUT(Doc.of(Level.FAULT) // + .text("Max stop time is exceeded")), // INVERTER_CURRENT_STATE_FAULT(Doc.of(Level.FAULT) // .text("The 'CurrentState' is invalid")), // + GRID_DISCONNECTION(Doc.of(Level.FAULT) // + .text("External grid protection disconnection (17)")), // ; private final Doc doc; @@ -92,59 +84,59 @@ public Doc doc() { public S64201CurrentState getCurrentState(); /** - * Gets the Channel for {@link ChannelId#MAX_START_ATTEMPTS}. + * Gets the Channel for {@link ChannelId#MAX_START_TIMEOUT}. * * @return the Channel */ - public default StateChannel getMaxStartAttemptsChannel() { - return this.channel(ChannelId.MAX_START_ATTEMPTS); + public default StateChannel getMaxStartTimeoutChannel() { + return this.channel(ChannelId.MAX_START_TIMEOUT); } /** - * Gets the {@link StateChannel} for {@link ChannelId#MAX_START_ATTEMPTS}. + * Gets the {@link StateChannel} for {@link ChannelId#MAX_START_TIMEOUT}. * * @return the Channel {@link Value} */ - public default Value getMaxStartAttempts() { - return this.getMaxStartAttemptsChannel().value(); + public default Value getMaxStartTimeout() { + return this.getMaxStartTimeoutChannel().value(); } /** - * Internal method to set the 'nextValue' on - * {@link ChannelId#MAX_START_ATTEMPTS} Channel. + * Internal method to set the 'nextValue' on {@link ChannelId#MAX_START_TIMEOUT} + * Channel. * * @param value the next value */ - public default void _setMaxStartAttempts(Boolean value) { - this.getMaxStartAttemptsChannel().setNextValue(value); + public default void _setMaxStartTimeout(boolean value) { + this.getMaxStartTimeoutChannel().setNextValue(value); } /** - * Gets the Channel for {@link ChannelId#MAX_STOP_ATTEMPTS}. + * Gets the Channel for {@link ChannelId#MAX_STOP_TIMEOUT}. * * @return the Channel */ - public default StateChannel getMaxStopAttemptsChannel() { - return this.channel(ChannelId.MAX_STOP_ATTEMPTS); + public default StateChannel getMaxStopTimeoutChannel() { + return this.channel(ChannelId.MAX_STOP_TIMEOUT); } /** - * Gets the {@link StateChannel} for {@link ChannelId#MAX_STOP_ATTEMPTS}. + * Gets the {@link StateChannel} for {@link ChannelId#MAX_STOP_TIMEOUT}. * * @return the Channel {@link Value} */ - public default Value getMaxStopAttempts() { - return this.getMaxStopAttemptsChannel().value(); + public default Value getMaxStopTimeout() { + return this.getMaxStopTimeoutChannel().value(); } /** - * Internal method to set the 'nextValue' on {@link ChannelId#MAX_STOP_ATTEMPTS} + * Internal method to set the 'nextValue' on {@link ChannelId#MAX_STOP_TIMEOUT} * Channel. * * @param value the next value */ - public default void _setMaxStopAttempts(Boolean value) { - this.getMaxStopAttemptsChannel().setNextValue(value); + public default void _setMaxStopTimeout(boolean value) { + this.getMaxStopTimeoutChannel().setNextValue(value); } /** @@ -166,4 +158,83 @@ public default WriteChannel getRequestedStateChannel() thr public default void setRequestedState(S64201RequestedState value) throws OpenemsNamedException { this.getRequestedStateChannel().setNextWriteValue(value); } + + /** + * Gets the Channel for ChannelId.INVERTER_CURRENT_STATE_FAULT. + * + * @return the Channel + */ + public default Channel getInverterCurrentStateFaultChannel() { + return this.channel(ChannelId.INVERTER_CURRENT_STATE_FAULT); + } + + /** + * Writes the value to the ChannelId.INVERTER_CURRENT_STATE_FAULT. + * + * @param value the next value + */ + public default void _setInverterCurrentStateFault(boolean value) { + this.getInverterCurrentStateFaultChannel().setNextValue(value); + } + + /** + * Gets the Channel for ChannelId.RUN_FAILED. + * + * @return the Channel + */ + public default Channel getRunFailedChannel() { + return this.channel(ChannelId.RUN_FAILED); + } + + /** + * Writes the value to the ChannelId.RUN_FAILED. + * + * @param value the next value + */ + public default void _setRunFailed(boolean value) { + this.getRunFailedChannel().setNextValue(value); + } + + /** + * Gets the Channel for ChannelId.GRID_DISCONNECTION. + * + * @return the Channel + */ + public default Channel getGridDisconnectionChannel() { + return this.channel(ChannelId.GRID_DISCONNECTION); + } + + /** + * Writes the value to the ChannelId.GRID_DISCONNECTION. + * + * @param value the next value + */ + public default void _setGridDisconnection(boolean value) { + this.getGridDisconnectionChannel().setNextValue(value); + } + + /** + * Checks if the system is in a running state. This method retrieves the + * system's global state and determines whether the system is in a running + * state. + * + * @return true if the system is in a running state, false otherwise. + */ + public boolean isRunning(); + + /** + * Checks if the system is in a stop state. This method retrieves the system's + * global state and determines whether the system is in a stop state. + * + * @return true if the system is in a stop state, false otherwise. + */ + public boolean isShutdown(); + + /** + * Checks if the system is in a fault state. This method retrieves the system's + * global state and determines whether the system is in a fault state. + * + * @return true if the system is in a fault state, false otherwise. + */ + public boolean hasFailure(); } diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImpl.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImpl.java index 95979bdc2d0..976d430e141 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImpl.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImpl.java @@ -6,7 +6,6 @@ import java.time.Instant; import java.util.Map; import java.util.Optional; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import org.osgi.service.cm.ConfigurationAdmin; @@ -33,6 +32,7 @@ import io.openems.edge.batteryinverter.api.ManagedSymmetricBatteryInverter; import io.openems.edge.batteryinverter.api.SymmetricBatteryInverter; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201CurrentState; +import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201StVnd; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64202.S64202EnLimit; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine.Context; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine.StateMachine; @@ -49,7 +49,6 @@ import io.openems.edge.common.channel.FloatWriteChannel; import io.openems.edge.common.channel.IntegerReadChannel; import io.openems.edge.common.channel.IntegerWriteChannel; -import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; @@ -106,6 +105,7 @@ protected void setModbus(BridgeModbus modbus) { * Kaco 92 does not have model 64203. */ private boolean hasSunSpecModel64203 = false; + private StartStop startStopTarget = StartStop.UNDEFINED; /** * Active SunSpec models for KACO blueplanet gridsave. Commented models are @@ -113,7 +113,7 @@ protected void setModbus(BridgeModbus modbus) { */ private static final Map ACTIVE_MODELS = ImmutableMap.builder() .put(DefaultSunSpecModel.S_1, Priority.LOW) // - .put(DefaultSunSpecModel.S_103, Priority.LOW) // + .put(DefaultSunSpecModel.S_103, Priority.HIGH) // .put(DefaultSunSpecModel.S_121, Priority.LOW) // .put(KacoSunSpecModel.S_64201, Priority.HIGH) // .put(KacoSunSpecModel.S_64202, Priority.LOW) // @@ -135,7 +135,7 @@ protected void setModbus(BridgeModbus modbus) { // .put(SunSpecModel.S_160, Priority.LOW) // @Activate - public BatteryInverterKacoBlueplanetGridsaveImpl() throws OpenemsException { + public BatteryInverterKacoBlueplanetGridsaveImpl() { super(// ACTIVE_MODELS, // OpenemsComponent.ChannelId.values(), // @@ -194,6 +194,9 @@ public void run(Battery battery, int setActivePower, int setReactivePower) throw // Set Battery Limits this.setBatteryLimits(battery); + // Set if there is grid disconnection failure + this.setGridDisconnectionFailure(); + // Calculate the Energy values from ActivePower. this.calculateEnergy(); @@ -202,24 +205,32 @@ public void run(Battery battery, int setActivePower, int setReactivePower) throw this.triggerWatchdog(); } - // Set State-Channels - this.setStateChannels(); - // Prepare Context - var context = new Context(this, battery, this.config, setActivePower, setReactivePower); + var context = new Context(this, // + battery, // + setActivePower, // + setReactivePower, // + this.componentManager.getClock()); // Call the StateMachine try { this.stateMachine.run(context); - - this.channel(BatteryInverterKacoBlueplanetGridsave.ChannelId.RUN_FAILED).setNextValue(false); - + this._setRunFailed(false); } catch (OpenemsNamedException e) { - this.channel(BatteryInverterKacoBlueplanetGridsave.ChannelId.RUN_FAILED).setNextValue(true); + this._setRunFailed(true); this.logError(this.log, "StateMachine failed: " + e.getMessage()); } } + private void setGridDisconnectionFailure() throws OpenemsException { + Channel stVndChannel = this.getSunSpecChannelOrError(KacoSunSpecModel.S64201.ST_VND); + Value stVnd = stVndChannel.value(); + if (!stVnd.isDefined()) { + return; + } + this._setGridDisconnection(stVnd.asEnum() == S64201StVnd.POWADORPROTECT_DISCONNECTION); + } + @Override public BatteryInverterConstraint[] getStaticConstraints() throws OpenemsException { if (this.stateMachine.getCurrentState() == State.RUNNING) { @@ -313,38 +324,6 @@ private void triggerWatchdog() throws OpenemsNamedException { } } - /** - * Sets the State-Channels, e.g. Warnings and Faults. - * - * @throws OpenemsNamedException on error - */ - private void setStateChannels() throws OpenemsNamedException { - /* - * INVERTER_CURRENT_STATE_FAULT - */ - StateChannel inverterCurrentStateChannel = this - .channel(BatteryInverterKacoBlueplanetGridsave.ChannelId.INVERTER_CURRENT_STATE_FAULT); - switch (this.getCurrentState()) { - case FAULT: - case UNDEFINED: - case NO_ERROR_PENDING: - inverterCurrentStateChannel.setNextValue(true); - break; - case GRID_CONNECTED: - case GRID_PRE_CONNECTED: - case MPPT: - case OFF: - case PRECHARGE: - case SHUTTING_DOWN: - case SLEEPING: - case STANDBY: - case STARTING: - case THROTTLED: - inverterCurrentStateChannel.setNextValue(false); - break; - } - } - /** * Mark SunSpec initialization completed; this takes some time at startup. */ @@ -405,34 +384,18 @@ public String debugLog() { .toString(); } - private final AtomicReference startStopTarget = new AtomicReference<>(StartStop.UNDEFINED); - @Override public void setStartStop(StartStop value) { - if (this.startStopTarget.getAndSet(value) != value) { - // Set only if value changed - this.stateMachine.forceNextState(State.UNDEFINED); - } + this.startStopTarget = value; } @Override public StartStop getStartStopTarget() { - switch (this.config.startStop()) { - case AUTO: - // read StartStop-Channel - return this.startStopTarget.get(); - - case START: - // force START - return StartStop.START; - - case STOP: - // force STOP - return StartStop.STOP; - } - - assert false; - return StartStop.UNDEFINED; // can never happen + return switch (this.config.startStop()) { + case AUTO -> this.startStopTarget; + case START -> StartStop.START; + case STOP -> StartStop.STOP; + }; } /** @@ -485,7 +448,7 @@ public Timedata getTimedata() { } @Override - protected void addBlock(int startAddress, SunSpecModel model, Priority priority) throws OpenemsException { + protected void addBlock(int startAddress, SunSpecModel model, Priority priority) { super.addBlock(startAddress, model, priority); // Mark S_64203 as available @@ -493,4 +456,23 @@ protected void addBlock(int startAddress, SunSpecModel model, Priority priority) this.hasSunSpecModel64203 = true; } } + + @Override + public boolean isRunning() { + return this.getCurrentState() == S64201CurrentState.GRID_CONNECTED// + || this.getCurrentState() == S64201CurrentState.THROTTLED; + } + + @Override + public boolean isShutdown() { + return this.getCurrentState() == S64201CurrentState.OFF // + || this.getCurrentState() == S64201CurrentState.STANDBY // + || this.getCurrentState() == S64201CurrentState.PRECHARGE// + || this.getCurrentState() == S64201CurrentState.SHUTTING_DOWN; + } + + @Override + public boolean hasFailure() { + return this.hasFaults() || this.getCurrentState() == S64201CurrentState.FAULT; + } } diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/KacoSunSpecModel.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/KacoSunSpecModel.java index 9f548478ea4..139f38ba225 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/KacoSunSpecModel.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/KacoSunSpecModel.java @@ -341,8 +341,8 @@ public static enum S64201 implements SunSpecPoint { PointType.INT16, // true, // AccessMode.READ_ONLY, // - Unit.HERTZ, // - "HZ_SF", // + Unit.MILLIHERTZ, // + "mHZ_SF", // new OptionsEnum[0])), // RESERVED_36(new ReservedPointImpl("S64201_RESERVED_36")), // RESERVED_37(new ReservedPointImpl("S64201_RESERVED_37")), // diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/Context.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/Context.java index 555e823f8be..9620e98ffba 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/Context.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/Context.java @@ -1,23 +1,31 @@ package io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine; +import java.time.Clock; +import java.time.Instant; + import io.openems.edge.battery.api.Battery; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.BatteryInverterKacoBlueplanetGridsave; -import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.Config; import io.openems.edge.common.statemachine.AbstractContext; public class Context extends AbstractContext { protected final Battery battery; - protected final Config config; protected final int setActivePower; protected final int setReactivePower; + protected final Clock clock; + + private static final int TIMEOUT = 240; // [s] - public Context(BatteryInverterKacoBlueplanetGridsave parent, Battery battery, Config config, int setActivePower, - int setReactivePower) { + public Context(BatteryInverterKacoBlueplanetGridsave parent, Battery battery, int setActivePower, + int setReactivePower, Clock clock) { super(parent); this.battery = battery; - this.config = config; this.setActivePower = setActivePower; this.setReactivePower = setReactivePower; + this.clock = clock; + } + + protected boolean isTimeout(Instant now, Instant entryAt) { + return now.minusSeconds(TIMEOUT).isAfter(entryAt); } } \ No newline at end of file diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/ErrorHandler.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/ErrorHandler.java index 5000e50caa6..8dc242a57b6 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/ErrorHandler.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/ErrorHandler.java @@ -1,8 +1,5 @@ package io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine; -import java.time.Duration; -import java.time.Instant; - import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201RequestedState; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine.StateMachine.State; @@ -10,50 +7,18 @@ public class ErrorHandler extends StateHandler { - private static final int WAIT_SECONDS = 120; - - private Instant entryAt = Instant.MIN; - @Override protected void onEntry(Context context) throws OpenemsNamedException { - this.entryAt = Instant.now(); + final var inverter = context.getParent(); + inverter.setRequestedState(S64201RequestedState.OFF); } @Override public State runAndGetNextState(Context context) throws OpenemsNamedException { - var inverter = context.getParent(); - switch (inverter.getCurrentState()) { - case STANDBY: - case GRID_CONNECTED: - case GRID_PRE_CONNECTED: - case THROTTLED: - case PRECHARGE: - case MPPT: - case STARTING: - case OFF: - case SHUTTING_DOWN: - case SLEEPING: - // no more error pending - return State.UNDEFINED; - case UNDEFINED: - // TODO - break; - case FAULT: - case NO_ERROR_PENDING: - /* - * According to Manual: to more errors to be acknowledged - try to turn OFF - */ - // TODO this should not be set all the time - inverter.setRequestedState(S64201RequestedState.OFF); - break; + final var inverter = context.getParent(); + if (!inverter.hasFailure()) { + return State.GO_STOPPED; } - - if (Duration.between(this.entryAt, Instant.now()).getSeconds() > WAIT_SECONDS) { - // Try again - return State.UNDEFINED; - } - return State.ERROR; } - } diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/GoRunningHandler.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/GoRunningHandler.java index 1779232b7ac..9ca5d58e034 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/GoRunningHandler.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/GoRunningHandler.java @@ -1,78 +1,47 @@ package io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine; -import java.time.Duration; import java.time.Instant; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.BatteryInverterKacoBlueplanetGridsave; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201RequestedState; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine.StateMachine.State; +import io.openems.edge.common.startstop.StartStop; import io.openems.edge.common.statemachine.StateHandler; public class GoRunningHandler extends StateHandler { - private Instant lastAttempt = Instant.MIN; - private int attemptCounter = 0; + private Instant entryAt = Instant.MIN; @Override - protected void onEntry(Context context) throws OpenemsNamedException { - this.lastAttempt = Instant.MIN; - this.attemptCounter = 0; - var inverter = context.getParent(); - inverter._setMaxStartAttempts(false); + protected void onEntry(Context context) { + this.entryAt = Instant.now(context.clock); } @Override public State runAndGetNextState(Context context) throws OpenemsNamedException { - var inverter = context.getParent(); - - // Has Faults -> abort - if (inverter.hasFaults()) { - return State.UNDEFINED; + final var inverter = context.getParent(); + if (inverter.hasFailure()) { + return State.ERROR; } - switch (inverter.getCurrentState()) { - case GRID_CONNECTED: - // All Good - - case THROTTLED: - // if inverter is throttled, full power is not available, but the device - // is still working - return State.RUNNING; + final var now = Instant.now(context.clock); + if (context.isTimeout(now, this.entryAt)) { + inverter._setMaxStartTimeout(true); + return State.ERROR; + } - case FAULT: - case GRID_PRE_CONNECTED: - case MPPT: - case NO_ERROR_PENDING: - case OFF: - case PRECHARGE: - case SHUTTING_DOWN: - case SLEEPING: - case STANDBY: - case STARTING: - case UNDEFINED: - // Not yet running... + if (inverter.getStartStopTarget() == StartStop.STOP) { + return State.GO_STOPPED; } - var isMaxStartTimePassed = Duration.between(this.lastAttempt, Instant.now()) - .getSeconds() > BatteryInverterKacoBlueplanetGridsave.RETRY_COMMAND_SECONDS; - if (!isMaxStartTimePassed) { - // Still waiting... - return State.GO_RUNNING; + if (inverter.isRunning()) { + return State.RUNNING; } - if (this.attemptCounter > BatteryInverterKacoBlueplanetGridsave.RETRY_COMMAND_MAX_ATTEMPTS) { - // Too many tries - inverter._setMaxStartAttempts(true); - return State.UNDEFINED; - } else { - // Trying to switch on - inverter.setRequestedState(S64201RequestedState.GRID_CONNECTED); - this.lastAttempt = Instant.now(); - this.attemptCounter++; - return State.GO_RUNNING; + // Trying to switch on + inverter.setRequestedState(S64201RequestedState.GRID_CONNECTED); + return State.GO_RUNNING; - } } } diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/GoStoppedHandler.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/GoStoppedHandler.java index d793a6dfea7..c28f540555d 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/GoStoppedHandler.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/GoStoppedHandler.java @@ -1,68 +1,46 @@ package io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine; -import java.time.Duration; import java.time.Instant; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.BatteryInverterKacoBlueplanetGridsave; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.KacoSunSpecModel.S64201.S64201RequestedState; import io.openems.edge.batteryinverter.kaco.blueplanetgridsave.statemachine.StateMachine.State; import io.openems.edge.common.statemachine.StateHandler; public class GoStoppedHandler extends StateHandler { - private Instant lastAttempt = Instant.MIN; - private int attemptCounter = 0; + private Instant entryAt = Instant.MIN; @Override protected void onEntry(Context context) { - this.lastAttempt = Instant.MIN; - this.attemptCounter = 0; + this.entryAt = Instant.now(context.clock); } @Override public State runAndGetNextState(Context context) throws OpenemsNamedException { - var inverter = context.getParent(); - - switch (inverter.getCurrentState()) { - case OFF: - case STANDBY: - // All Good - return State.STOPPED; - - case GRID_CONNECTED: - case THROTTLED: - case FAULT: - case GRID_PRE_CONNECTED: - case MPPT: - case NO_ERROR_PENDING: - case PRECHARGE: - case SHUTTING_DOWN: - case SLEEPING: - case STARTING: - case UNDEFINED: - // Not yet running... + final var inverter = context.getParent(); + + // Due to the low battery DC voltage, the inverter receives a battery low + // voltage error in the stop process and goes into a fault state that can be + // ignored during this time. Due to this situation, hasFault is preferred + // instead of hasFailure. + if (inverter.hasFaults()) { + inverter._setInverterCurrentStateFault(true); + return State.ERROR; } - var isMaxStartTimePassed = Duration.between(this.lastAttempt, Instant.now()) - .getSeconds() > BatteryInverterKacoBlueplanetGridsave.RETRY_COMMAND_SECONDS; - if (!isMaxStartTimePassed) { - // Still waiting... - return State.GO_STOPPED; + final var now = Instant.now(context.clock); + if (context.isTimeout(now, this.entryAt)) { + inverter._setMaxStartTimeout(true); + return State.ERROR; } - if (this.attemptCounter > BatteryInverterKacoBlueplanetGridsave.RETRY_COMMAND_MAX_ATTEMPTS) { - // Too many tries - inverter._setMaxStopAttempts(true); - return State.UNDEFINED; - - } else { - // Trying to switch off - inverter.setRequestedState(S64201RequestedState.OFF); - this.lastAttempt = Instant.now(); - this.attemptCounter++; - return State.GO_STOPPED; + if (inverter.isShutdown()) { + return State.STOPPED; } - } + // Trying to switch off + inverter.setRequestedState(S64201RequestedState.OFF); + return State.GO_STOPPED; + } } diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/RunningHandler.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/RunningHandler.java index 76301531cb8..986127e902e 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/RunningHandler.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/RunningHandler.java @@ -15,39 +15,16 @@ public class RunningHandler extends StateHandler { @Override public State runAndGetNextState(Context context) throws OpenemsNamedException { var inverter = context.getParent(); - - if (inverter.hasFaults()) { - return State.UNDEFINED; + if (inverter.hasFailure() || !inverter.isRunning()) { + return State.ERROR; } - switch (inverter.getCurrentState()) { - case FAULT: - case GRID_PRE_CONNECTED: - case MPPT: - case NO_ERROR_PENDING: - case OFF: - case PRECHARGE: - case SHUTTING_DOWN: - case SLEEPING: - case STANDBY: - case STARTING: - case UNDEFINED: - return State.UNDEFINED; - - case GRID_CONNECTED: - // All Good - - case THROTTLED: - // if inverter is throttled, full power is not available, but the device - // is still working + if (inverter.getStartStopTarget() == StartStop.STOP) { + return State.GO_STOPPED; } - // Mark as started - inverter._setStartStop(StartStop.START); - - // Apply Active and Reactive Power Set-Points this.applyPower(context); - + inverter._setStartStop(StartStop.START); return State.RUNNING; } @@ -75,5 +52,4 @@ private void applyPower(Context context) throws OpenemsNamedException { var varSetPct = context.setReactivePower * 100F / maxApparentPower; varSetPctChannel.setNextWriteValue(varSetPct); } - } diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/StateMachine.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/StateMachine.java index 743dca4fe1a..ab70913b54d 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/StateMachine.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/StateMachine.java @@ -51,20 +51,13 @@ public StateMachine(State initialState) { @Override public StateHandler getStateHandler(State state) { - switch (state) { - case UNDEFINED: - return new UndefinedHandler(); - case GO_RUNNING: - return new GoRunningHandler(); - case RUNNING: - return new RunningHandler(); - case GO_STOPPED: - return new GoStoppedHandler(); - case STOPPED: - return new StoppedHandler(); - case ERROR: - return new ErrorHandler(); - } - throw new IllegalArgumentException("Unknown State [" + state + "]"); + return switch (state) { + case UNDEFINED -> new UndefinedHandler(); + case GO_RUNNING -> new GoRunningHandler(); + case RUNNING -> new RunningHandler(); + case GO_STOPPED -> new GoStoppedHandler(); + case STOPPED -> new StoppedHandler(); + case ERROR -> new ErrorHandler(); + }; } } diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/StoppedHandler.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/StoppedHandler.java index 75a415711ed..c2a6b23cca2 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/StoppedHandler.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/StoppedHandler.java @@ -8,11 +8,17 @@ public class StoppedHandler extends StateHandler { @Override public State runAndGetNextState(Context context) { - // Mark as stopped - var inverter = context.getParent(); - inverter._setStartStop(StartStop.STOP); + final var inverter = context.getParent(); + + if (inverter.hasFaults()) { + return State.ERROR; + } + + if (inverter.getStartStopTarget() == StartStop.START) { + return State.GO_RUNNING; + } + inverter._setStartStop(StartStop.STOP); return State.STOPPED; } - } diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/UndefinedHandler.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/UndefinedHandler.java index bd6b4927bac..c177bd4538a 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/UndefinedHandler.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/src/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/statemachine/UndefinedHandler.java @@ -7,30 +7,24 @@ public class UndefinedHandler extends StateHandler { @Override public State runAndGetNextState(Context context) { - var inverter = context.getParent(); + final var inverter = context.getParent(); - switch (inverter.getStartStopTarget()) { - case UNDEFINED: - // Stuck in UNDEFINED State + if (inverter.getCurrentState().isUndefined()) { return State.UNDEFINED; + } - case START: - // force START - if (inverter.hasFaults()) { - // Has Faults -> error handling - return State.ERROR; - } else { - // No Faults -> start - return State.GO_RUNNING; - } - - case STOP: - // force STOP - return State.GO_STOPPED; + if (inverter.hasFailure()) { + return State.ERROR; } - assert false; - return State.UNDEFINED; // can never happen - } + if (inverter.isRunning()) { + return State.RUNNING; + } + if (inverter.isShutdown()) { + return State.STOPPED; + } + + return State.GO_STOPPED; + } } diff --git a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/test/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImplTest.java b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/test/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImplTest.java index 7a17c2a68a0..d4d2c215065 100644 --- a/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/test/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImplTest.java +++ b/io.openems.edge.batteryinverter.kaco.blueplanetgridsave/test/io/openems/edge/batteryinverter/kaco/blueplanetgridsave/BatteryInverterKacoBlueplanetGridsaveImplTest.java @@ -90,6 +90,7 @@ public void prepareTest() throws Exception { addChannel.invoke(sut, KacoSunSpecModel.S64201.WATCHDOG.getChannelId()); addChannel.invoke(sut, KacoSunSpecModel.S64201.W_SET_PCT.getChannelId()); addChannel.invoke(sut, KacoSunSpecModel.S64201.WPARAM_RMP_TMS.getChannelId()); + addChannel.invoke(sut, KacoSunSpecModel.S64201.ST_VND.getChannelId()); test.activate(MyConfig.create() // .setId(BATTERY_INVERTER_ID) // @@ -108,7 +109,7 @@ public void testStart() throws Exception { .output(STATE_MACHINE, State.UNDEFINED)) // .next(new TestCase() // .timeleap(clock, 4, ChronoUnit.SECONDS) // - .output(STATE_MACHINE, State.GO_RUNNING)) // + .output(STATE_MACHINE, State.STOPPED)) // .next(new TestCase() // .timeleap(clock, 1, ChronoUnit.SECONDS) // .input(CURRENT_STATE, S64201CurrentState.GRID_CONNECTED) // diff --git a/io.openems.edge.batteryinverter.refu88k/src/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88kImpl.java b/io.openems.edge.batteryinverter.refu88k/src/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88kImpl.java index d311907ea13..a63d14f76fd 100644 --- a/io.openems.edge.batteryinverter.refu88k/src/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88kImpl.java +++ b/io.openems.edge.batteryinverter.refu88k/src/io/openems/edge/batteryinverter/refu88k/BatteryInverterRefuStore88kImpl.java @@ -220,7 +220,7 @@ public int getPowerPrecision() { private static final int SUNSPEC_64800 = 40225; // MESA-PCS Extensions @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { // Register + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(SUNSPEC_1, Priority.LOW, // m(BatteryInverterRefuStore88k.ChannelId.ID_1, new UnsignedWordElement(SUNSPEC_1)), // 40002 diff --git a/io.openems.edge.batteryinverter.sinexcel/src/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcelImpl.java b/io.openems.edge.batteryinverter.sinexcel/src/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcelImpl.java index b41b9e77a2c..5a65149220b 100644 --- a/io.openems.edge.batteryinverter.sinexcel/src/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcelImpl.java +++ b/io.openems.edge.batteryinverter.sinexcel/src/io/openems/edge/batteryinverter/sinexcel/BatteryInverterSinexcelImpl.java @@ -391,7 +391,7 @@ public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(1, Priority.HIGH, // m(BatteryInverterSinexcel.ChannelId.MANUFACTURER_AND_MODEL_NUMBER, // diff --git a/io.openems.edge.batteryinverter.sunspec/src/io/openems/edge/batteryinverter/sunspec/AbstractSunSpecBatteryInverter.java b/io.openems.edge.batteryinverter.sunspec/src/io/openems/edge/batteryinverter/sunspec/AbstractSunSpecBatteryInverter.java index 646ca7f453b..6e4d8e8a637 100644 --- a/io.openems.edge.batteryinverter.sunspec/src/io/openems/edge/batteryinverter/sunspec/AbstractSunSpecBatteryInverter.java +++ b/io.openems.edge.batteryinverter.sunspec/src/io/openems/edge/batteryinverter/sunspec/AbstractSunSpecBatteryInverter.java @@ -23,7 +23,7 @@ public abstract class AbstractSunSpecBatteryInverter extends AbstractOpenemsSunS public AbstractSunSpecBatteryInverter(Map activeModels, io.openems.edge.common.channel.ChannelId[] firstInitialChannelIds, - io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) throws OpenemsException { + io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) { super(activeModels, firstInitialChannelIds, furtherInitialChannelIds); } diff --git a/io.openems.edge.bridge.http/src/io/openems/edge/bridge/http/dummy/DummyBridgeHttp.java b/io.openems.edge.bridge.http/src/io/openems/edge/bridge/http/dummy/DummyBridgeHttp.java index 7a5e3af2b5c..5abccd97132 100644 --- a/io.openems.edge.bridge.http/src/io/openems/edge/bridge/http/dummy/DummyBridgeHttp.java +++ b/io.openems.edge.bridge.http/src/io/openems/edge/bridge/http/dummy/DummyBridgeHttp.java @@ -1,27 +1,52 @@ package io.openems.edge.bridge.http.dummy; +import static java.util.concurrent.CompletableFuture.completedFuture; + +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; import io.openems.edge.bridge.http.api.BridgeHttp; public class DummyBridgeHttp implements BridgeHttp { + public final List cycleEndpoints = new ArrayList<>(); + public final List timeEndpoints = new ArrayList<>(); + + private String nextRequestResult = null; + @Override public void subscribeCycle(CycleEndpoint endpoint) { - // TODO Auto-generated method stub - + this.cycleEndpoints.add(endpoint); } @Override public void subscribeTime(TimeEndpoint endpoint) { - // TODO Auto-generated method stub - + this.timeEndpoints.add(endpoint); } @Override public CompletableFuture request(Endpoint endpoint) { - // TODO Auto-generated method stub - return null; + return completedFuture(this.nextRequestResult); + } + + /** + * Mocks a result for all {@link CycleEndpoint}s. + * + * @param result the mocked read result + */ + public void mockCycleResult(String result) { + this.cycleEndpoints.forEach(// + e -> e.result().accept(result)); + } + + /** + * Mocks a result for simple request {@link Endpoint}. + * + * @param nextRequestResult the mocked read result + */ + public void mockRequestResult(String nextRequestResult) { + this.nextRequestResult = nextRequestResult; } } diff --git a/io.openems.edge.bridge.http/src/io/openems/edge/bridge/http/dummy/DummyBridgeHttpFactory.java b/io.openems.edge.bridge.http/src/io/openems/edge/bridge/http/dummy/DummyBridgeHttpFactory.java index d8a4d19c59a..f19ec95eca8 100644 --- a/io.openems.edge.bridge.http/src/io/openems/edge/bridge/http/dummy/DummyBridgeHttpFactory.java +++ b/io.openems.edge.bridge.http/src/io/openems/edge/bridge/http/dummy/DummyBridgeHttpFactory.java @@ -11,16 +11,25 @@ public class DummyBridgeHttpFactory extends BridgeHttpFactory { + public final DummyBridgeHttp bridge = new DummyBridgeHttp(); + public DummyBridgeHttpFactory() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { super(); - ReflectionUtils.setAttribute(BridgeHttpFactory.class, this, "csoBridgeHttp", new DummyBridgeHttpCso()); + ReflectionUtils.setAttribute(BridgeHttpFactory.class, this, "csoBridgeHttp", + new DummyBridgeHttpCso(this.bridge)); } private static class DummyBridgeHttpCso implements ComponentServiceObjects { + private final DummyBridgeHttp bridge; + + public DummyBridgeHttpCso(DummyBridgeHttp bridge) { + this.bridge = bridge; + } + @Override public BridgeHttp getService() { - return new DummyBridgeHttp(); + return this.bridge; } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusSerialImpl.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusSerialImpl.java index 4488593accc..d36835f41f8 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusSerialImpl.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/BridgeModbusSerialImpl.java @@ -60,14 +60,20 @@ public class BridgeModbusSerialImpl extends AbstractModbusBridge /** The configured parity. */ private Parity parity; - + /** Enable internal bus termination. */ private boolean enableTermination; - - /** The configured delay between activating the transmitter and actually sending data in microseconds. */ + + /** + * The configured delay between activating the transmitter and actually sending + * data in microseconds. + */ private int delayBeforeTx; - - /** The configured delay between the end of transmitting data and deactivating transmitter in microseconds. */ + + /** + * The configured delay between the end of transmitting data and deactivating + * transmitter in microseconds. + */ private int delayAfterTx; public BridgeModbusSerialImpl() { diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigSerial.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigSerial.java index 5032b4f574c..0ae88b2bbdd 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigSerial.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/ConfigSerial.java @@ -35,13 +35,13 @@ @AttributeDefinition(name = "Parity", description = "The parity - 'none', 'even', 'odd', 'mark' or 'space'") Parity parity() default Parity.NONE; - + @AttributeDefinition(name = "Enable termination", description = "Sets whether the interface shall enable internal bus termination") boolean enableTermination() default true; - + @AttributeDefinition(name = "Delay before TX [μs]", description = "Sets the delay between activating the transmitter and actually sending data. There are devices in the field requiring such a delay for start bit detection.", min = "0") int delayBeforeTx() default 1000; - + @AttributeDefinition(name = "Delay after TX [μs]", description = "Sets the delay between the end of transmitting data and deactivating the transmitter.", min = "0") int delayAfterTx() default 0; diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/AbstractOpenemsModbusComponent.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/AbstractOpenemsModbusComponent.java index b429a7177cb..7bf6e9e249d 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/AbstractOpenemsModbusComponent.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/AbstractOpenemsModbusComponent.java @@ -230,7 +230,7 @@ public BridgeModbus getBridgeModbus() { * @return the {@link ModbusProtocol} * @throws OpenemsException on error */ - protected ModbusProtocol getModbusProtocol() throws OpenemsException { + protected ModbusProtocol getModbusProtocol() { var protocol = this.protocol; if (protocol != null) { return protocol; @@ -251,7 +251,7 @@ public void retryModbusCommunication() { * @return the ModbusProtocol * @throws OpenemsException on error */ - protected abstract ModbusProtocol defineModbusProtocol() throws OpenemsException; + protected abstract ModbusProtocol defineModbusProtocol(); /** * Maps an Element to one or more ModbusChannels using converters, that convert diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusProtocol.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusProtocol.java index e4e922aead0..3b6cac58e6c 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusProtocol.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusProtocol.java @@ -1,6 +1,5 @@ package io.openems.edge.bridge.modbus.api; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.bridge.modbus.api.task.Task; import io.openems.edge.common.taskmanager.TasksManager; @@ -21,9 +20,8 @@ public class ModbusProtocol { * * @param parent the {@link AbstractOpenemsModbusComponent} parent * @param tasks the {@link Task}s - * @throws OpenemsException on error */ - public ModbusProtocol(AbstractOpenemsModbusComponent parent, Task... tasks) throws OpenemsException { + public ModbusProtocol(AbstractOpenemsModbusComponent parent, Task... tasks) { this.parent = parent; this.addTasks(tasks); } @@ -32,9 +30,8 @@ public ModbusProtocol(AbstractOpenemsModbusComponent parent, Task... tasks) thro * Adds Tasks to the Protocol. * * @param tasks the tasks - * @throws OpenemsException on error */ - public synchronized void addTasks(Task... tasks) throws OpenemsException { + public synchronized void addTasks(Task... tasks) { for (Task task : tasks) { this.addTask(task); } @@ -44,9 +41,8 @@ public synchronized void addTasks(Task... tasks) throws OpenemsException { * Adds a Task to the Protocol. * * @param task the task - * @throws OpenemsException on plausibility error */ - public synchronized void addTask(Task task) throws OpenemsException { + public synchronized void addTask(Task task) { // add the the parent to the Task task.setParent(this.parent); // fill taskManager diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusUtils.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusUtils.java index e2ebb682479..48dc3a52b99 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusUtils.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/ModbusUtils.java @@ -1,166 +1,133 @@ package io.openems.edge.bridge.modbus.api; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; +import static io.openems.edge.bridge.modbus.api.task.Task.ExecuteState.NO_OP; +import static java.util.Collections.emptyList; +import static java.util.concurrent.CompletableFuture.completedFuture; + import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiPredicate; import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.IntStream; +import java.util.stream.Stream; import com.ghgande.j2mod.modbus.procimg.InputRegister; import com.ghgande.j2mod.modbus.procimg.Register; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.bridge.modbus.api.element.ModbusRegisterElement; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; import io.openems.edge.bridge.modbus.api.task.Task; +import io.openems.edge.bridge.modbus.api.task.Task.ExecuteState; import io.openems.edge.common.taskmanager.Priority; public class ModbusUtils { + /** + * Predefined `retryPredicate` that triggers a retry whenever `value` is null, + * i.e. on any error. + * + * @param the Type of the element + * @param executeState the Task {@link ExecuteState} + * @param value the value + * @return true for retry + */ + public static boolean retryOnNull(ExecuteState executeState, T value) { + return value == null; + } + + /** + * Predefined `retryPredicate` that never retries. + * + * @param the Type of the element + * @param executeState the Task {@link ExecuteState} + * @param value the value + * @return always false + */ + public static boolean doNotRetry(ExecuteState executeState, T value) { + return false; + } + /** * Reads given Element once from Modbus. * - * @param the Type of the element - * @param modbusProtocol the {@link ModbusProtocol}, that is linked with a - * {@link BridgeModbus} - * @param element the {@link ModbusRegisterElement} - * @param tryAgainOnError if true, tries to read till it receives a value; if - * false, stops after first try and possibly return null + * @param the Type of the element + * @param modbusProtocol the {@link ModbusProtocol}, that is linked with a + * {@link BridgeModbus} + * @param retryPredicate yield true to retry reading values; false otherwise. + * Parameters are the {@link ExecuteState} of the entire + * task and the individual element value + * @param element the {@link ModbusRegisterElement} * @return a future value, e.g. a Integer or null (if tryAgainOnError is false) - * @throws OpenemsException on error with the {@link ModbusProtocol} object */ - public static CompletableFuture readELementOnce(ModbusProtocol modbusProtocol, - ModbusRegisterElement element, boolean tryAgainOnError) throws OpenemsException { - // Prepare result - final var result = new CompletableFuture(); - - // Activate task - final Task task = new FC3ReadRegistersTask(element.startAddress, Priority.HIGH, element); - modbusProtocol.addTask(task); - - // Register listener for element - element.onUpdateCallback(value -> { - if (value == null) { - if (tryAgainOnError) { - return; - } - result.complete(null); - } - // do not try again - modbusProtocol.removeTask(task); - result.complete(value); - }); - - return result; + @SuppressWarnings("unchecked") + public static CompletableFuture readElementOnce(ModbusProtocol modbusProtocol, + BiPredicate retryPredicate, ModbusRegisterElement element) { + return readElementsOnce(modbusProtocol, retryPredicate, // + new ModbusRegisterElement[] { element }) // + .thenApply(rsr -> ((ReadElementsResult) rsr).values().get(0)); } /** * Reads given Elements once from Modbus. * - * @param the Type of the elements - * @param modbusProtocol the {@link ModbusProtocol}, that is linked with a - * {@link BridgeModbus} - * @param elements the {@link ModbusRegisterElement}s - * @param tryAgainOnError if true, tries to read till it receives a value on - * first register; if false, stops after first try and - * possibly return null - * @return a future array of values, e.g. Integer[] or null (if tryAgainOnError - * is false). If an array is returned, it is guaranteed to have the same - * length as `elements` - * @throws OpenemsException on error with the {@link ModbusProtocol} object + * @param the Type of the elements + * @param modbusProtocol the {@link ModbusProtocol}, that is linked with a + * {@link BridgeModbus} + * @param retryPredicate yield true to retry reading values. Parameters are the + * Task success state and individual element value + * @param elements the {@link ModbusRegisterElement}s + * @return a future array of values, e.g. Integer[] or null. If an array is + * returned, it is guaranteed to have the same length as `elements` */ - public static CompletableFuture> readELementsOnce(ModbusProtocol modbusProtocol, - ModbusRegisterElement[] elements, boolean tryAgainOnError) throws OpenemsException { + @SafeVarargs + public static CompletableFuture> readElementsOnce(ModbusProtocol modbusProtocol, + BiPredicate retryPredicate, ModbusRegisterElement... elements) { if (elements.length == 0) { - return CompletableFuture.completedFuture(Collections.emptyList()); + return completedFuture(new ReadElementsResult<>(NO_OP, emptyList())); } - // Prepare result - final var result = new CompletableFuture>(); + // Register listener for each element + final var executeState = new AtomicReference(ExecuteState.NO_OP); // Activate task - final Task task = new FC3ReadRegistersTask(elements[0].startAddress, Priority.HIGH, elements); + final Task task = new FC3ReadRegistersTask(executeState::set, // + elements[0].startAddress, Priority.HIGH, elements); modbusProtocol.addTask(task); - // Register listener for each element - final var subResults = new ArrayList>(); - { + @SuppressWarnings("unchecked") + final var subResults = (CompletableFuture[]) new CompletableFuture[elements.length]; + for (var i = 0; i < elements.length; i++) { var subResult = new CompletableFuture(); - subResults.add(subResult); - elements[0].onUpdateCallback(value -> { - if (value == null) { - if (tryAgainOnError) { - // try again - return; - } else { - result.complete(null); - } - } - - // do not try again - modbusProtocol.removeTask(task); - subResult.complete(value); - }); - } - - for (var i = 1; i < elements.length; i++) { - var subResult = new CompletableFuture(); - subResults.add(subResult); + subResults[i] = subResult; elements[i].onUpdateCallback(value -> { - modbusProtocol.removeTask(task); - subResult.complete(value); + if (retryPredicate.test(executeState.get(), value)) { + // try again + return; + } else { + // do not try again + subResult.complete(value); + } }); } - CompletableFuture // - .allOf(subResults.toArray(new CompletableFuture[subResults.size()])) // - .thenAccept(ignored -> result.complete(// - subResults.stream() // - .map(CompletableFuture::join) // - .toList())); - - return result; + return CompletableFuture // + .allOf(subResults) // + .thenApply(ignore -> { + // remove task + modbusProtocol.removeTask(task); + + // return combined future + return new ReadElementsResult<>(executeState.get(), // + Stream.of(subResults) // + .map(CompletableFuture::join) // + .toList()); + }); } - /** - * Converts upper/lower bytes to Short. - * - * @param value the int value - * @param upperBytes 1 = upper two bytes, 0 = lower two bytes - * @return the Short - */ - public static Short convert(int value, int upperBytes) { - var b = ByteBuffer.allocate(4); - b.order(ByteOrder.LITTLE_ENDIAN); - b.putInt(value); - - var byte0 = b.get(upperBytes * 2); - var byte1 = b.get(upperBytes * 2 + 1); - - var shortBuf = ByteBuffer.allocate(2); - shortBuf.order(ByteOrder.LITTLE_ENDIAN); - shortBuf.put(0, byte0); - shortBuf.put(1, byte1); + public static record ReadElementsResult(ExecuteState executeState, List values) { - return shortBuf.getShort(); - } - - /** - * Converts a byte array to a String in the form "00C1 00B2". - * - * @param data byte array - * @return string - */ - public static String byteArrayToHexString(byte[] data) { - return IntStream.range(0, data.length / 2) // - .mapToObj(i -> String.format("%2s%2s", // - Integer.toHexString(data[i]), Integer.toHexString(data[i + 1])).replace(' ', '0')) - .collect(Collectors.joining(" ")); } /** diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadDigitalInputsTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadDigitalInputsTask.java index a7a6cec7edc..5a918fe1d5c 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadDigitalInputsTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadDigitalInputsTask.java @@ -1,5 +1,6 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -17,9 +18,9 @@ public abstract class AbstractReadDigitalInputsTask // extends AbstractReadTask { - public AbstractReadDigitalInputsTask(String name, Class responseClazz, int startAddress, - Priority priority, CoilElement... elements) { - super(name, responseClazz, CoilElement.class, startAddress, priority, elements); + public AbstractReadDigitalInputsTask(String name, Consumer onExecute, Class responseClazz, + int startAddress, Priority priority, CoilElement... elements) { + super(name, onExecute, responseClazz, CoilElement.class, startAddress, priority, elements); } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadRegistersTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadRegistersTask.java index 936971a7464..24f691d6c1e 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadRegistersTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadRegistersTask.java @@ -1,6 +1,7 @@ package io.openems.edge.bridge.modbus.api.task; import java.util.Arrays; +import java.util.function.Consumer; import com.ghgande.j2mod.modbus.msg.ModbusRequest; import com.ghgande.j2mod.modbus.msg.ModbusResponse; @@ -17,9 +18,9 @@ public abstract class AbstractReadRegistersTask // extends AbstractReadTask { - public AbstractReadRegistersTask(String name, Class responseClazz, int startAddress, Priority priority, - ModbusElement... elements) { - super(name, responseClazz, ModbusRegisterElement.class, startAddress, priority, elements); + public AbstractReadRegistersTask(String name, Consumer onExecute, Class responseClazz, + int startAddress, Priority priority, ModbusElement... elements) { + super(name, onExecute, responseClazz, ModbusRegisterElement.class, startAddress, priority, elements); } @SuppressWarnings("unchecked") diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadTask.java index 66c104f6fe8..ef5c010c268 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractReadTask.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import java.util.stream.Stream; import org.slf4j.Logger; @@ -33,9 +34,9 @@ public abstract class AbstractReadTask elementClazz; - public AbstractReadTask(String name, Class responseClazz, Class elementClazz, int startAddress, - Priority priority, ModbusElement... elements) { - super(name, responseClazz, startAddress, elements); + public AbstractReadTask(String name, Consumer onExecute, Class responseClazz, + Class elementClazz, int startAddress, Priority priority, ModbusElement... elements) { + super(name, onExecute, responseClazz, startAddress, elements); this.elementClazz = elementClazz; this.priority = priority; } @@ -49,19 +50,26 @@ public ExecuteState execute(AbstractModbusBridge bridge) { try { var result = this.parseResponse(response); validateResponse(result, this.length); + + // NOTE: onExecute has to be called before filling elements; but OK could be + // wrong if fillElements throws an exception. + this.onExecute.accept(ExecuteState.OK); this.fillElements(result); + return ExecuteState.OK; + } catch (OpenemsException e1) { logError(this.log, e1, "Parsing Response failed."); throw e1; } - return ExecuteState.OK; } catch (Exception e) { + var executeState = new ExecuteState.Error(e); + this.onExecute.accept(executeState); + // Invalidate Elements Stream.of(this.elements).forEach(el -> el.invalidate(bridge)); - - return ExecuteState.ERROR; + return executeState; } } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractTask.java index 60ac8520114..35c419f539e 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractTask.java @@ -3,6 +3,7 @@ import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; +import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,6 +31,7 @@ public abstract non-sealed class AbstractTask implements Task { protected final String name; + protected final Consumer onExecute; protected final Class responseClazz; protected final int startAddress; protected final int length; @@ -39,8 +41,10 @@ public abstract non-sealed class AbstractTask responseClazz, int startAddress, ModbusElement... elements) { + public AbstractTask(String name, Consumer onExecute, Class responseClazz, int startAddress, + ModbusElement... elements) { this.name = name; + this.onExecute = onExecute; this.responseClazz = responseClazz; this.startAddress = startAddress; this.elements = elements; @@ -87,7 +91,7 @@ public AbstractOpenemsModbusComponent getParent() { * WriteTask. * * @param bridge the Modbus-Bridge - * @return the number of executed Sub-Tasks + * @return the {@link ExecuteState} */ public abstract ExecuteState execute(AbstractModbusBridge bridge); diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractWriteTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractWriteTask.java index 40460ca1f75..e8d2335c259 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractWriteTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/AbstractWriteTask.java @@ -1,5 +1,7 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.function.Consumer; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,8 +18,9 @@ public abstract class AbstractWriteTask // extends AbstractTask implements WriteTask { - public AbstractWriteTask(String name, Class responseClazz, int startAddress, ModbusElement... elements) { - super(name, responseClazz, startAddress, elements); + public AbstractWriteTask(String name, Consumer onExecute, Class responseClazz, + int startAddress, ModbusElement... elements) { + super(name, onExecute, responseClazz, startAddress, elements); } /** @@ -45,19 +48,26 @@ public abstract static class Single responseClazz, int startAddress, ELEMENT element) { - super(name, responseClazz, startAddress, element); + public Single(String name, Consumer onExecute, Class responseClazz, int startAddress, + ELEMENT element) { + super(name, onExecute, responseClazz, startAddress, element); this.element = element; } @Override public final ExecuteState execute(AbstractModbusBridge bridge) { + var result = this._execute(bridge); + this.onExecute.accept(result); + return result; + } + + private ExecuteState _execute(AbstractModbusBridge bridge) { final REQUEST request; try { request = this.createModbusRequest(); } catch (OpenemsException e) { logError(this.log, e, "Creating Modbus Request failed."); - return ExecuteState.ERROR; + return new ExecuteState.Error(e); } if (request == null) { @@ -70,7 +80,7 @@ public final ExecuteState execute(AbstractModbusBridge bridge) { } catch (Exception e) { // On error a log message has already been logged - return ExecuteState.ERROR; + return new ExecuteState.Error(e); } } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC16WriteRegistersTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC16WriteRegistersTask.java index 0c16e83e897..7767f54674b 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC16WriteRegistersTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC16WriteRegistersTask.java @@ -13,6 +13,7 @@ import com.ghgande.j2mod.modbus.msg.WriteMultipleRegistersResponse; import com.ghgande.j2mod.modbus.procimg.Register; +import io.openems.common.utils.FunctionUtils; import io.openems.edge.bridge.modbus.api.AbstractModbusBridge; import io.openems.edge.bridge.modbus.api.LogVerbosity; import io.openems.edge.bridge.modbus.api.ModbusUtils; @@ -29,11 +30,21 @@ public class FC16WriteRegistersTask private final Logger log = LoggerFactory.getLogger(FC16WriteRegistersTask.class); public FC16WriteRegistersTask(int startAddress, ModbusElement... elements) { - super("FC16WriteRegisters", WriteMultipleRegistersResponse.class, startAddress, elements); + this(FunctionUtils::doNothing, startAddress, elements); + } + + public FC16WriteRegistersTask(Consumer onExecute, int startAddress, ModbusElement... elements) { + super("FC16WriteRegisters", onExecute, WriteMultipleRegistersResponse.class, startAddress, elements); } @Override - public ExecuteState execute(AbstractModbusBridge bridge) { + public final ExecuteState execute(AbstractModbusBridge bridge) { + var result = this._execute(bridge); + this.onExecute.accept(result); + return result; + } + + private ExecuteState _execute(AbstractModbusBridge bridge) { var requests = mergeWriteRegisters(this.elements, message -> this.log.warn(message)).stream() // .map(e -> new WriteMultipleRegistersRequest(e.startAddress(), e.getRegisters())) // .toList(); @@ -42,7 +53,7 @@ public ExecuteState execute(AbstractModbusBridge bridge) { return ExecuteState.NO_OP; } - boolean hasError = false; + Exception lastError = null; for (var request : requests) { try { this.executeRequest(bridge, request); @@ -52,12 +63,12 @@ public ExecuteState execute(AbstractModbusBridge bridge) { // Invalidate Elements Stream.of(this.elements).forEach(el -> el.invalidate(bridge)); - hasError = true; + lastError = e; } } - if (hasError) { - return ExecuteState.ERROR; + if (lastError != null) { + return new ExecuteState.Error(lastError); } else { return ExecuteState.OK; } diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC1ReadCoilsTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC1ReadCoilsTask.java index 859db8748aa..0c546569033 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC1ReadCoilsTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC1ReadCoilsTask.java @@ -1,9 +1,12 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.function.Consumer; + import com.ghgande.j2mod.modbus.msg.ReadCoilsRequest; import com.ghgande.j2mod.modbus.msg.ReadCoilsResponse; import com.ghgande.j2mod.modbus.util.BitVector; +import io.openems.common.utils.FunctionUtils; import io.openems.edge.bridge.modbus.api.element.CoilElement; import io.openems.edge.common.taskmanager.Priority; @@ -14,7 +17,12 @@ public class FC1ReadCoilsTask extends AbstractReadDigitalInputsTask { public FC1ReadCoilsTask(int startAddress, Priority priority, CoilElement... elements) { - super("FC1ReadCoils", ReadCoilsResponse.class, startAddress, priority, elements); + this(FunctionUtils::doNothing, startAddress, priority, elements); + } + + public FC1ReadCoilsTask(Consumer onExecute, int startAddress, Priority priority, + CoilElement... elements) { + super("FC1ReadCoils", onExecute, ReadCoilsResponse.class, startAddress, priority, elements); } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC2ReadInputsTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC2ReadInputsTask.java index bd42892fa01..462dad3b5d0 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC2ReadInputsTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC2ReadInputsTask.java @@ -1,9 +1,12 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.function.Consumer; + import com.ghgande.j2mod.modbus.msg.ReadInputDiscretesRequest; import com.ghgande.j2mod.modbus.msg.ReadInputDiscretesResponse; import com.ghgande.j2mod.modbus.util.BitVector; +import io.openems.common.utils.FunctionUtils; import io.openems.edge.bridge.modbus.api.element.CoilElement; import io.openems.edge.common.taskmanager.Priority; @@ -15,7 +18,12 @@ public class FC2ReadInputsTask extends AbstractReadDigitalInputsTask { public FC2ReadInputsTask(int startAddress, Priority priority, CoilElement... elements) { - super("FC2ReadCoils", ReadInputDiscretesResponse.class, startAddress, priority, elements); + this(FunctionUtils::doNothing, startAddress, priority, elements); + } + + public FC2ReadInputsTask(Consumer onExecute, int startAddress, Priority priority, + CoilElement... elements) { + super("FC2ReadCoils", onExecute, ReadInputDiscretesResponse.class, startAddress, priority, elements); } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC3ReadRegistersTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC3ReadRegistersTask.java index d1d104a28fd..420c406e0c7 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC3ReadRegistersTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC3ReadRegistersTask.java @@ -1,10 +1,13 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.function.Consumer; + import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersRequest; import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersResponse; import com.ghgande.j2mod.modbus.procimg.Register; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.FunctionUtils; import io.openems.edge.bridge.modbus.api.ModbusUtils; import io.openems.edge.bridge.modbus.api.element.ModbusElement; import io.openems.edge.common.taskmanager.Priority; @@ -17,7 +20,13 @@ public class FC3ReadRegistersTask extends AbstractReadRegistersTask { public FC3ReadRegistersTask(int startAddress, Priority priority, ModbusElement... elements) { - super("FC3ReadHoldingRegisters", ReadMultipleRegistersResponse.class, startAddress, priority, elements); + this(FunctionUtils::doNothing, startAddress, priority, elements); + } + + public FC3ReadRegistersTask(Consumer onExecute, int startAddress, Priority priority, + ModbusElement... elements) { + super("FC3ReadHoldingRegisters", onExecute, ReadMultipleRegistersResponse.class, startAddress, priority, + elements); } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC4ReadInputRegistersTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC4ReadInputRegistersTask.java index e05190fd759..96d383e7859 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC4ReadInputRegistersTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC4ReadInputRegistersTask.java @@ -1,5 +1,6 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.function.Consumer; import java.util.stream.Stream; import com.ghgande.j2mod.modbus.msg.ReadInputRegistersRequest; @@ -8,6 +9,7 @@ import com.ghgande.j2mod.modbus.procimg.SimpleRegister; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.FunctionUtils; import io.openems.edge.bridge.modbus.api.ModbusUtils; import io.openems.edge.bridge.modbus.api.element.ModbusElement; import io.openems.edge.common.taskmanager.Priority; @@ -20,7 +22,12 @@ public class FC4ReadInputRegistersTask extends AbstractReadRegistersTask { public FC4ReadInputRegistersTask(int startAddress, Priority priority, ModbusElement... elements) { - super("FC4ReadInputRegisters", ReadInputRegistersResponse.class, startAddress, priority, elements); + this(FunctionUtils::doNothing, startAddress, priority, elements); + } + + public FC4ReadInputRegistersTask(Consumer onExecute, int startAddress, Priority priority, + ModbusElement... elements) { + super("FC4ReadInputRegisters", onExecute, ReadInputRegistersResponse.class, startAddress, priority, elements); } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC5WriteCoilTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC5WriteCoilTask.java index 25c3d3de17a..5f42a0069af 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC5WriteCoilTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC5WriteCoilTask.java @@ -1,9 +1,12 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.function.Consumer; + import com.ghgande.j2mod.modbus.msg.WriteCoilRequest; import com.ghgande.j2mod.modbus.msg.WriteCoilResponse; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.FunctionUtils; import io.openems.edge.bridge.modbus.api.element.CoilElement; /** @@ -13,7 +16,11 @@ public class FC5WriteCoilTask extends AbstractWriteTask.Single { public FC5WriteCoilTask(int startAddress, CoilElement element) { - super("FC5WriteCoil", WriteCoilResponse.class, startAddress, element); + this(FunctionUtils::doNothing, startAddress, element); + } + + public FC5WriteCoilTask(Consumer onExecute, int startAddress, CoilElement element) { + super("FC5WriteCoil", onExecute, WriteCoilResponse.class, startAddress, element); } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC6WriteRegisterTask.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC6WriteRegisterTask.java index c116f3907ab..ceff91159bd 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC6WriteRegisterTask.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/FC6WriteRegisterTask.java @@ -1,9 +1,12 @@ package io.openems.edge.bridge.modbus.api.task; +import java.util.function.Consumer; + import com.ghgande.j2mod.modbus.msg.WriteSingleRegisterRequest; import com.ghgande.j2mod.modbus.msg.WriteSingleRegisterResponse; import io.openems.common.exceptions.OpenemsException; +import io.openems.common.utils.FunctionUtils; import io.openems.edge.bridge.modbus.api.ModbusUtils; import io.openems.edge.bridge.modbus.api.element.AbstractSingleWordElement; @@ -11,7 +14,12 @@ public class FC6WriteRegisterTask extends AbstractWriteTask.Single> { public FC6WriteRegisterTask(int startAddress, AbstractSingleWordElement element) { - super("FC6WriteRegister", WriteSingleRegisterResponse.class, startAddress, element); + this(FunctionUtils::doNothing, startAddress, element); + } + + public FC6WriteRegisterTask(Consumer onExecute, int startAddress, + AbstractSingleWordElement element) { + super("FC6WriteRegister", onExecute, WriteSingleRegisterResponse.class, startAddress, element); } @Override diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/Task.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/Task.java index f810b548f94..2862df554ab 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/Task.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/task/Task.java @@ -58,12 +58,27 @@ public sealed interface Task extends ManagedTask permits AbstractTask, ReadTask, */ public ExecuteState execute(AbstractModbusBridge bridge); - public static enum ExecuteState { + public static sealed interface ExecuteState { + + public static final class Ok implements ExecuteState { + private Ok() { + } + } + /** Successfully executed request(s). */ - OK, + public static final ExecuteState.Ok OK = new ExecuteState.Ok(); + + public static final class NoOp implements ExecuteState { + private NoOp() { + } + } + /** No available requests -> no operation. */ - NO_OP, + public static final ExecuteState.NoOp NO_OP = new ExecuteState.NoOp(); + /** Executing request(s) failed. */ - ERROR; + public static final record Error(Exception exception) implements ExecuteState { + } + } } \ No newline at end of file diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/worker/ModbusWorker.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/worker/ModbusWorker.java index f51191f8631..68aed664b5d 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/worker/ModbusWorker.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/api/worker/ModbusWorker.java @@ -73,23 +73,21 @@ protected void forever() throws InterruptedException { // execute the task var result = this.execute.apply(task); - switch (result) { - case OK -> { + // NOTE: with Java 21 LTS this can be refactored to a pattern matching switch + // statement + if (result instanceof ExecuteState.Ok) { // no exception & at least one sub-task executed this.markComponentAsDefective(task.getParent(), false); - } - case ERROR -> { + } else if (result instanceof ExecuteState.NoOp) { + // did not execute anything + + } else if (result instanceof ExecuteState.Error) { this.markComponentAsDefective(task.getParent(), true); // invalidate elements of this task this.invalidate.accept(task.getElements()); } - - case NO_OP -> { - } - } - } /** diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponent.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponent.java index 5dafbd32b4d..a45c8251a35 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponent.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponent.java @@ -1,5 +1,10 @@ package io.openems.edge.bridge.modbus.sunspec; +import static com.ghgande.j2mod.modbus.Modbus.ILLEGAL_ADDRESS_EXCEPTION; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementsOnce; +import static java.util.concurrent.CompletableFuture.completedFuture; + import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -15,6 +20,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.ghgande.j2mod.modbus.ModbusSlaveException; import com.google.common.collect.Lists; import io.openems.common.exceptions.OpenemsException; @@ -25,13 +31,12 @@ import io.openems.edge.bridge.modbus.api.ModbusUtils; import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; import io.openems.edge.bridge.modbus.api.element.ModbusElement; -import io.openems.edge.bridge.modbus.api.element.ModbusRegisterElement; import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.bridge.modbus.api.task.AbstractTask; import io.openems.edge.bridge.modbus.api.task.FC16WriteRegistersTask; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; -import io.openems.edge.bridge.modbus.api.task.Task; +import io.openems.edge.bridge.modbus.api.task.Task.ExecuteState; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.taskmanager.Priority; @@ -70,7 +75,7 @@ public abstract class AbstractOpenemsSunSpecComponent extends AbstractOpenemsMod */ public AbstractOpenemsSunSpecComponent(Map activeModels, io.openems.edge.common.channel.ChannelId[] firstInitialChannelIds, - io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) throws OpenemsException { + io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) { super(firstInitialChannelIds, furtherInitialChannelIds); this.activeModels = activeModels; this.modbusProtocol = new ModbusProtocol(this); @@ -97,18 +102,10 @@ protected boolean activate(ComponentContext context, String id, String alias, bo throw new IllegalArgumentException("This modbus device is not SunSpec!"); } - try { - this.readNextBlock(40_002, expectedBlocks).thenRun(() -> { - this.isSunSpecInitializationCompleted = true; - this.onSunSpecInitializationCompleted(); - }); - - } catch (OpenemsException e) { - this.logWarn(this.log, "Error while reading SunSpec identifier block: " + e.getMessage()); - e.printStackTrace(); + this.readNextBlock(40_002, expectedBlocks).thenRun(() -> { this.isSunSpecInitializationCompleted = true; this.onSunSpecInitializationCompleted(); - } + }); }); return super.activate(context, id, alias, enabled, unitId, cm, modbusReference, modbusId); } @@ -128,16 +125,8 @@ protected final ModbusProtocol defineModbusProtocol() { * @throws OpenemsException on error */ private CompletableFuture isSunSpec() throws OpenemsException { - final var result = new CompletableFuture(); - ModbusUtils.readELementOnce(this.modbusProtocol, new UnsignedDoublewordElement(40_000), true) - .thenAccept(value -> { - if (value == 0x53756e53) { - result.complete(true); - } else { - result.complete(false); - } - }); - return result; + return readElementOnce(this.modbusProtocol, ModbusUtils::retryOnNull, new UnsignedDoublewordElement(40_000)) // + .thenApply(v -> v == 0x53756e53); } /** @@ -148,31 +137,47 @@ private CompletableFuture isSunSpec() throws OpenemsException { * @return a future that completes once reading the block finished * @throws OpenemsException on error */ - private CompletableFuture readNextBlock(int startAddress, Set remainingBlocks) - throws OpenemsException { - final var finished = new CompletableFuture(); - + private CompletableFuture readNextBlock(int startAddress, Set remainingBlocks) { // Finish if all expected Blocks have been read if (remainingBlocks.isEmpty()) { - finished.complete(null); + return completedFuture(null); } /* * Try to read block by block until all required blocks have been read or an - * END_OF_MAP register has been found. + * END_OF_MAP register has been found or reading fails permanently. * * It may still happen that a device does not have a valid END_OF_MAP register * and that some blocks are not read - especially when one component is used for * multiple devices like single and three phase inverter. */ - this.readElementsOnceTyped(new UnsignedWordElement(startAddress), new UnsignedWordElement(startAddress + 1)) - .thenAccept(values -> { - int blockId = values.get(0); + return readElementsOnce(this.modbusProtocol, // + // Retry if value is null and error is not "Illegal Data Address". + // Background: some SMA inverters do not provide an END_OF_MAP register. + (executeState, value) -> { + if (executeState instanceof ExecuteState.Error s) { + if (s.exception() instanceof ModbusSlaveException mse) { + if (mse.isType(ILLEGAL_ADDRESS_EXCEPTION)) { + return false; // do not retry + } + } + } + if (value != null) { + return false; // do not retry + } + return true; + }, // + + new UnsignedWordElement(startAddress), // Block-ID + new UnsignedWordElement(startAddress + 1)) // Length of Block + + .thenCompose(rer -> { + var values = rer.values(); + var blockId = values.get(0); // END_OF_MAP - if (blockId == 0xFFFF) { - finished.complete(null); - return; + if (blockId == null || blockId == 0xFFFF) { + return completedFuture(null); } // Handle SunSpec Block @@ -190,46 +195,23 @@ private CompletableFuture readNextBlock(int startAddress, Set rem if (activeEntry != null) { var sunSpecModel = activeEntry.getKey(); var priority = activeEntry.getValue(); - try { - this.addBlock(startAddress, sunSpecModel, priority); - remainingBlocks.remove(activeEntry.getKey().getBlockId()); - } catch (OpenemsException e) { - this.logWarn(this.log, "Error while adding SunSpec-Model [" + blockId - + "] starting at [" + startAddress + "]: " + e.getMessage()); - e.printStackTrace(); - } + this.addBlock(startAddress, sunSpecModel, priority); + remainingBlocks.remove(activeEntry.getKey().getBlockId()); } else { // This block is not considered, because the Model is not active this.logInfo(this.log, "Ignoring SunSpec-Model [" + blockId + "] starting at [" + startAddress + "]"); } - } - // Stop reading if all expectedBlocks have been read - if (remainingBlocks.isEmpty()) { - finished.complete(null); - return; } // Read next block recursively var nextBlockStartAddress = startAddress + 2 + values.get(1); - try { - - final var readNextBlockFuture = this.readNextBlock(nextBlockStartAddress, remainingBlocks); - // Announce finished when next block (recursively) is finished - readNextBlockFuture.thenRun(() -> { - finished.complete(null); - }); - } catch (OpenemsException e) { - this.logWarn(this.log, "Error while adding SunSpec-Model [" + blockId + "] starting at [" - + startAddress + "]: " + e.getMessage()); - e.printStackTrace(); - finished.complete(null); // announce finish immediately to not get stuck - } + // Announce finished when next block (recursively) is finished + return this.readNextBlock(nextBlockStartAddress, remainingBlocks); }); - return finished; } /** @@ -286,9 +268,8 @@ public boolean isSunSpecInitializationCompleted() { * @param startAddress the address to start reading from * @param model the SunSpecModel * @param priority the reading priority - * @throws OpenemsException on error */ - protected void addBlock(int startAddress, SunSpecModel model, Priority priority) throws OpenemsException { + protected void addBlock(int startAddress, SunSpecModel model, Priority priority) { this.logInfo(this.log, "Adding SunSpec-Model [" + model.getBlockId() + ":" + model.label() + "] starting at [" + startAddress + "]"); var readElements = new ArrayList(); @@ -424,54 +405,6 @@ protected ElementToChannelConverter generateElementToChannelConverter(SunSpecMod } } - /** - * Reads given Elements once from Modbus. - * - * @param the Type of the elements - * @param elements the elements - * @return a future list with the values, e.g. a list of integers - * @throws OpenemsException on error - */ - @SafeVarargs - private final CompletableFuture> readElementsOnceTyped(ModbusRegisterElement... elements) - throws OpenemsException { - // Register listeners for elements - @SuppressWarnings("unchecked") - final var subResults = (CompletableFuture[]) new CompletableFuture[elements.length]; - for (var i = 0; i < elements.length; i++) { - var subResult = new CompletableFuture(); - subResults[i] = subResult; - - var element = elements[i]; - element.onUpdateCallback(value -> { - if (value == null) { - // try again - return; - } - subResult.complete(value); - }); - } - - // Activate task - final Task task = new FC3ReadRegistersTask(elements[0].startAddress, Priority.HIGH, elements); - this.modbusProtocol.addTask(task); - - // Prepare result - final var result = new CompletableFuture>(); - CompletableFuture.allOf(subResults).thenRun(() -> { - // do not try again - this.modbusProtocol.removeTask(task); - - // get all results and complete result - List values = Stream.of(subResults) // - .map(CompletableFuture::join) // - .collect(Collectors.toCollection(ArrayList::new)); - result.complete(values); - }); - - return result; - } - /** * Get the Channel for the given Point. * @@ -524,7 +457,7 @@ protected void mapFirstPointToChannel(io.openems.edge.common.channel.ChannelId t for (SunSpecPoint point : points) { Optional> c = this.getSunSpecChannel(point); if (c.isPresent()) { - c.get().onUpdate(value -> { + c.get().onSetNextValue(value -> { this.channel(targetChannel).setNextValue(converter.elementToChannel(value.get())); }); return; diff --git a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/test/DummyModbusBridge.java b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/test/DummyModbusBridge.java index cd031cfb4cb..1d508bb9363 100644 --- a/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/test/DummyModbusBridge.java +++ b/io.openems.edge.bridge.modbus/src/io/openems/edge/bridge/modbus/test/DummyModbusBridge.java @@ -23,6 +23,10 @@ public class DummyModbusBridge extends AbstractModbusBridge implements BridgeMod private InetAddress ipAddress = null; public DummyModbusBridge(String id) { + this(id, LogVerbosity.NONE); + } + + public DummyModbusBridge(String id, LogVerbosity logVerbosity) { super(// OpenemsComponent.ChannelId.values(), // BridgeModbus.ChannelId.values(), // @@ -31,7 +35,7 @@ public DummyModbusBridge(String id) { for (Channel channel : this.channels()) { channel.nextProcessImage(); } - super.activate(null, id, "", true, LogVerbosity.NONE, 2); + super.activate(null, id, "", true, logVerbosity, 2); } /** @@ -66,12 +70,12 @@ public InetAddress getIpAddress() { @Override public ModbusTransaction getNewModbusTransaction() throws OpenemsException { - throw new UnsupportedOperationException("Unsupported by Dummy Class"); + throw new UnsupportedOperationException("getNewModbusTransaction() Unsupported by Dummy Class"); } @Override public void closeModbusConnection() { - throw new UnsupportedOperationException("Unsupported by Dummy Class"); + throw new UnsupportedOperationException("closeModbusConnection() Unsupported by Dummy Class"); } } diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/BridgeModbusTcpImplTest.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/BridgeModbusTcpImplTest.java index ca9a516b620..276dc8dcce6 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/BridgeModbusTcpImplTest.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/BridgeModbusTcpImplTest.java @@ -22,7 +22,6 @@ import io.openems.edge.common.taskmanager.Priority; import io.openems.edge.common.test.AbstractComponentTest.TestCase; import io.openems.edge.common.test.ComponentTest; -import io.openems.edge.common.test.DummyCycle; import io.openems.edge.common.test.TestUtils; public class BridgeModbusTcpImplTest { @@ -61,7 +60,6 @@ public void test() throws Exception { var device = new MyModbusComponent(DEVICE_ID, sut, UNIT_ID); var test = new ComponentTest(sut) // .addComponent(device) // - .addReference("cycle", new DummyCycle(CYCLE_TIME)) // .activate(MyConfigTcp.create() // .setId(MODBUS_ID) // .setIp("127.0.0.1") // @@ -135,11 +133,10 @@ public Doc doc() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(100, Priority.HIGH, // - m(ChannelId.REGISTER_100, new UnsignedWordElement(100) // - ))); // + m(ChannelId.REGISTER_100, new UnsignedWordElement(100)))); // } } diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/DummyModbusComponent.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/DummyModbusComponent.java index f0b8ee7d3e6..a4f3dc43db3 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/DummyModbusComponent.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/DummyModbusComponent.java @@ -50,12 +50,12 @@ public DummyModbusComponent(String id, AbstractModbusBridge bridge, int unitId, super.activate(context, id, "", true, unitId, cm, "Modbus", bridge.id()); } - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this); } @Override - public ModbusProtocol getModbusProtocol() throws OpenemsException { + public ModbusProtocol getModbusProtocol() { return super.getModbusProtocol(); } diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/MyConfigSerial.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/MyConfigSerial.java index 5df6db2a1d2..fbb13ca6a46 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/MyConfigSerial.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/MyConfigSerial.java @@ -53,17 +53,17 @@ public Builder setParity(Parity parity) { this.parity = parity; return this; } - + public Builder setEnableTermination(boolean enableTermination) { this.enableTermination = enableTermination; return this; } - + public Builder setDelayBeforeTx(int delay) { this.delayBeforeTx = delay; return this; } - + public Builder setDelayAfterTx(int delay) { this.delayAfterTx = delay; return this; @@ -124,7 +124,7 @@ public Stopbit stopbits() { public Parity parity() { return this.builder.parity; } - + @Override public boolean enableTermination() { return this.builder.enableTermination; diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/MyConfigTcp.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/MyConfigTcp.java index 18a2114d244..1f74849a5ce 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/MyConfigTcp.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/MyConfigTcp.java @@ -6,7 +6,7 @@ @SuppressWarnings("all") public class MyConfigTcp extends AbstractComponentConfig implements ConfigTcp { - protected static class Builder { + public static class Builder { private String id; private String ip; private int port; diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/ConversionTest.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/ConversionTest.java index 0e502a04d72..5e5b5777c16 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/ConversionTest.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/ConversionTest.java @@ -10,25 +10,6 @@ public class ConversionTest { - @Test - public void testShortConversions() { - - var v1 = 0; - var result = ModbusUtils.convert(v1, 0); - Short expected = 0; - assertEquals(expected, result); - - v1 = 1; - result = ModbusUtils.convert(v1, 0); - expected = 1; - assertEquals(expected, result); - - v1 = 65536; - result = ModbusUtils.convert(v1, 1); - expected = 1; - assertEquals(expected, result); - } - @Test public void multiplyTest() { var value = 10; diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/ModbusUtilsTest.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/ModbusUtilsTest.java new file mode 100644 index 00000000000..afca83ff9b3 --- /dev/null +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/ModbusUtilsTest.java @@ -0,0 +1,47 @@ +package io.openems.edge.bridge.modbus.api; + +import static io.openems.edge.bridge.modbus.api.ModbusUtils.doNotRetry; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.intToHexString; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.registersToHexString; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.retryOnNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.ghgande.j2mod.modbus.procimg.SimpleInputRegister; +import com.ghgande.j2mod.modbus.procimg.SimpleRegister; + +import io.openems.edge.bridge.modbus.api.task.Task.ExecuteState; + +public class ModbusUtilsTest { + + @Test + public void testRetryOnNull() { + assertTrue(retryOnNull(ExecuteState.OK, null)); + assertFalse(retryOnNull(ExecuteState.OK, 123)); + } + + @Test + public void testDoNotRetry() { + assertFalse(doNotRetry(ExecuteState.OK, null)); + assertFalse(doNotRetry(ExecuteState.OK, 123)); + } + + @Test + public void testIntToHexString() { + assertEquals("00af", intToHexString(0xAF)); + } + + @Test + public void testRegistersToHexString() { + assertEquals("00aa 00ff", registersToHexString(new SimpleRegister(0xAA), new SimpleRegister(0xFF))); + } + + @Test + public void testInputRegistersToHexString() { + assertEquals("00aa 00ff", registersToHexString(new SimpleInputRegister(0xAA), new SimpleInputRegister(0xFF))); + } + +} diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/AbstractDummyTask.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/AbstractDummyTask.java index b834c62cb6c..5a61d27e0b4 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/AbstractDummyTask.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/AbstractDummyTask.java @@ -1,11 +1,14 @@ package io.openems.edge.bridge.modbus.api.worker; +import java.util.function.Consumer; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ghgande.j2mod.modbus.msg.ModbusRequest; import com.ghgande.j2mod.modbus.msg.ModbusResponse; +import io.openems.common.utils.FunctionUtils; import io.openems.edge.bridge.modbus.api.AbstractModbusBridge; import io.openems.edge.bridge.modbus.api.task.AbstractTask; @@ -15,22 +18,28 @@ public abstract class AbstractDummyTask extends AbstractTask onExecute, long delay) { + super(name, onExecute, ModbusResponse.class, 0); this.name = name; this.delay = delay; } - public void setDefective(boolean isDefective, long delay) { - this.isDefective = isDefective; + public void setDefective(Exception defect, long delay) { + this.defect = defect; this.delay = delay; } + protected long getDelay() { + return this.delay; + } + @Override protected String payloadToString(ModbusRequest request) { return ""; @@ -41,29 +50,16 @@ protected String payloadToString(ModbusResponse response) { return ""; } - /** - * Callback on Execute. - * - * @param onExecuteCallback the callback {@link Runnable} - */ - public void onExecute(Runnable onExecuteCallback) { - this.onExecuteCallback = onExecuteCallback; - } - @Override public ExecuteState execute(AbstractModbusBridge bridge) { - if (this.onExecuteCallback != null) { - this.onExecuteCallback.run(); - } - try { Thread.sleep(this.delay); } catch (InterruptedException e) { this.log.warn(e.getMessage()); } - if (this.isDefective) { - return ExecuteState.ERROR; + if (this.defect != null) { + return new ExecuteState.Error(this.defect); } return ExecuteState.OK; } diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/DummyReadTask.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/DummyReadTask.java index 63357905cfb..d8b3284429c 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/DummyReadTask.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/DummyReadTask.java @@ -14,7 +14,7 @@ public DummyReadTask(String name, long delay, Priority priority) { @Override public String toString() { - return "DummyReadTask [name=" + this.name + ", delay=" + this.delay + ", priority=" + this.priority + "]"; + return "DummyReadTask [name=" + this.name + ", delay=" + this.getDelay() + ", priority=" + this.priority + "]"; } @Override diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/DummyWriteTask.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/DummyWriteTask.java index c219e0a6295..f7ff37d8c51 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/DummyWriteTask.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/api/worker/DummyWriteTask.java @@ -11,7 +11,7 @@ public DummyWriteTask(String name, long delay) { @Override public String toString() { - return "DummyWriteTask [name=" + this.name + ", delay=" + this.delay + "]"; + return "DummyWriteTask [name=" + this.name + ", delay=" + this.getDelay() + "]"; } @Override diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponentTest.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponentTest.java index ed2041643b3..a4526ef63dd 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponentTest.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/AbstractOpenemsSunSpecComponentTest.java @@ -1,14 +1,35 @@ package io.openems.edge.bridge.modbus.sunspec; +import static io.openems.edge.bridge.modbus.sunspec.AbstractOpenemsSunSpecComponent.preprocessModbusElements; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import java.util.ArrayList; +import java.util.stream.IntStream; +import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; +import com.ghgande.j2mod.modbus.procimg.SimpleProcessImage; +import com.ghgande.j2mod.modbus.procimg.SimpleRegister; +import com.ghgande.j2mod.modbus.slave.ModbusSlave; +import com.ghgande.j2mod.modbus.slave.ModbusSlaveFactory; +import com.google.common.collect.ImmutableSortedMap; + import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.bridge.modbus.BridgeModbusTcpImpl; +import io.openems.edge.bridge.modbus.MyConfigTcp; +import io.openems.edge.bridge.modbus.api.LogVerbosity; import io.openems.edge.bridge.modbus.api.element.ModbusElement; import io.openems.edge.bridge.modbus.api.element.StringWordElement; +import io.openems.edge.bridge.modbus.sunspec.dummy.MyConfig; +import io.openems.edge.bridge.modbus.sunspec.dummy.MySunSpecComponentImpl; +import io.openems.edge.common.test.AbstractComponentTest.TestCase; +import io.openems.edge.common.test.ComponentTest; +import io.openems.edge.common.test.DummyConfigurationAdmin; +import io.openems.edge.common.test.TestUtils; public class AbstractOpenemsSunSpecComponentTest { @@ -22,11 +43,129 @@ public void testPreprocessModbusElements() throws OpenemsException { elements.add(element); } - var sut = AbstractOpenemsSunSpecComponent.preprocessModbusElements(elements); + var sut = preprocessModbusElements(elements); assertEquals(2, sut.size()); // two sublists assertEquals(69, sut.get(0).size()); // first task assertEquals(1, sut.get(1).size()); // second task assertEquals(StringWordElement.class, sut.get(1).get(0).getClass()); // second task } + private static final int UNIT_ID = 1; + + @Before + public void changeLogLevel() { + java.lang.System.setProperty("org.ops4j.pax.logging.DefaultServiceLog.level", "INFO"); + } + + private static ImmutableSortedMap.Builder generateSunSpec() { + var b = ImmutableSortedMap.naturalOrder() // + .put(40000, 0x5375) // SunSpec identifier + .put(40001, 0x6e53) // SunSpec identifier + + .put(40002, 1) // SunSpec Block-ID + .put(40003, 66); // Length of the SunSpec Block + IntStream.range(40004, 40070).forEach(i -> b.put(i, 0)); + b // + .put(40070, 123) // SunSpec Block-ID + .put(40071, 24); // Length of the SunSpec Block + IntStream.range(40072, 40096).forEach(i -> b.put(i, 0)); + b // + .put(40096, 999) // SunSpec Block-ID + .put(40097, 10) // Length of the SunSpec Block + + .put(40108, 702) // SunSpec Block-ID + .put(40109, 50); // Length of the SunSpec Block + IntStream.range(40110, 40160).forEach(i -> b.put(i, 0)); + return b; + } + + // Disabled because of timing issues in CI + @Ignore + @Test + public void test() throws Exception { + var port = TestUtils.findRandomOpenPortOnAllLocalInterfaces(); + ModbusSlave slave = null; + try { + /* + * Open Modbus/TCP Slave + */ + slave = ModbusSlaveFactory.createTCPSlave(port, 1); + slave.open(); + + /* + * Instantiate Modbus-Bridge + */ + var bridge = new BridgeModbusTcpImpl(); + var testBridge = new ComponentTest(bridge) // + .activate(MyConfigTcp.create() // + .setId("modbus0") // + .setIp("127.0.0.1") // + .setPort(port) // + .setInvalidateElementsAfterReadErrors(1) // + .setLogVerbosity(LogVerbosity.READS_AND_WRITES_VERBOSE) // + .build()); + + var cmp = new MySunSpecComponentImpl(); + var testCmp = new ComponentTest(cmp) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("setModbus", bridge) // + .activate(MyConfig.create() // + .setId("cmp0") // + .setModbusId("modbus0") // + .setModbusUnitId(UNIT_ID) // + .setReadFromModbusBlock(1) // + .build()); + + testWithEndOfMap(slave, bridge, testBridge, cmp, testCmp); + testWithIllegalAddress(slave, bridge, testBridge, cmp, testCmp); + + assertFalse(cmp.getSunSpecChannel(DefaultSunSpecModel.S103.APH_A).isPresent()); + assertNotNull(cmp.getSunSpecChannelOrError(DefaultSunSpecModel.S702.W_MAX_RTG)); + + } finally { + if (slave != null) { + slave.close(); + } + } + } + + private static void cycle(ComponentTest testBridge, ComponentTest testCmp, int count, int sleep) throws Exception { + for (var i = 0; i < count; i++) { + testBridge.next(new TestCase()); + testCmp.next(new TestCase()); + Thread.sleep(sleep); // TODO required? + } + } + + private static void testWithEndOfMap(ModbusSlave slave, BridgeModbusTcpImpl bridge, ComponentTest testBridge, + MySunSpecComponentImpl cmp, ComponentTest testCmp) throws Exception { + var processImage = new SimpleProcessImage(UNIT_ID); + generateSunSpec() // + .put(40160, 0xFFFF) // END_OF_MAP + .put(40161, 0)// Behind the end + .build() // + .entrySet().stream() // + .forEach(e -> processImage.addRegister(e.getKey(), new SimpleRegister(e.getValue()))); + slave.addProcessImage(UNIT_ID, processImage); + + cycle(testBridge, testCmp, 5, 100); + + assertEquals(58, cmp.channels().size()); + } + + private static void testWithIllegalAddress(ModbusSlave slave, BridgeModbusTcpImpl bridge, ComponentTest testBridge, + MySunSpecComponentImpl cmp, ComponentTest testCmp) throws Exception { + var processImage = new SimpleProcessImage(UNIT_ID); + generateSunSpec() // + .build() // + .entrySet().stream() // + .forEach(e -> processImage.addRegister(e.getKey(), new SimpleRegister(e.getValue()))); + slave.addProcessImage(UNIT_ID, processImage); + + cycle(testBridge, testCmp, 2, 100); + cycle(testBridge, testCmp, 1, 2000); // wait for defective component + cycle(testBridge, testCmp, 2, 100); + + assertEquals(58, cmp.channels().size()); + } } diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/DummySunSpecComponent.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/DummySunSpecComponent.java index adbb4e21c59..3a6d52f2fc4 100644 --- a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/DummySunSpecComponent.java +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/DummySunSpecComponent.java @@ -16,17 +16,20 @@ public class DummySunSpecComponent extends AbstractOpenemsSunSpecComponent { /** * All models are active with low priority. */ - private static final Map ACTIVE_MODELS = Stream.of(DefaultSunSpecModel.values()) - .collect(Collectors.toMap(model -> model, model -> Priority.LOW, (a, b) -> a, TreeMap::new)); + private static final Map ACTIVE_MODELS = Stream.of(DefaultSunSpecModel.values()) // + .collect(Collectors.toMap(// + model -> model, // + model -> Priority.LOW, // + (a, b) -> a, TreeMap::new)); - public DummySunSpecComponent() throws OpenemsException { + public DummySunSpecComponent() { super(ACTIVE_MODELS, // OpenemsComponent.ChannelId.values(), // ModbusComponent.ChannelId.values()); // this.addBlocks(); } - private void addBlocks() throws OpenemsException { + private void addBlocks() { var startAddress = 40000; for (var entry : ACTIVE_MODELS.keySet()) { this.addBlock(startAddress, entry, ACTIVE_MODELS.get(entry)); diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/dummy/Config.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/dummy/Config.java new file mode 100644 index 00000000000..24169ea7b76 --- /dev/null +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/dummy/Config.java @@ -0,0 +1,19 @@ +package io.openems.edge.bridge.modbus.sunspec.dummy; + +@interface Config { + String id(); + + String alias(); + + boolean enabled(); + + boolean readOnly(); + + String modbus_id(); + + int modbusUnitId(); + + int readFromModbusBlock(); + + String Modbus_target(); +} \ No newline at end of file diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/dummy/MyConfig.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/dummy/MyConfig.java new file mode 100644 index 00000000000..665e48e73be --- /dev/null +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/dummy/MyConfig.java @@ -0,0 +1,90 @@ +package io.openems.edge.bridge.modbus.sunspec.dummy; + +import io.openems.common.test.AbstractComponentConfig; +import io.openems.common.utils.ConfigUtils; + +@SuppressWarnings("all") +public class MyConfig extends AbstractComponentConfig implements Config { + + public static class Builder { + private String id = null; + private boolean readOnly; + private String modbusId = null; + private int modbusUnitId; + private int readFromModbusBlock; + + private Builder() { + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + return this; + } + + public Builder setModbusId(String modbusId) { + this.modbusId = modbusId; + return this; + } + + public Builder setModbusUnitId(int modbusUnitId) { + this.modbusUnitId = modbusUnitId; + return this; + } + + public Builder setReadFromModbusBlock(int readFromModbusBlock) { + this.readFromModbusBlock = readFromModbusBlock; + return this; + } + + public MyConfig build() { + return new MyConfig(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new Builder(); + } + + private final Builder builder; + + private MyConfig(Builder builder) { + super(Config.class, builder.id); + this.builder = builder; + } + + @Override + public boolean readOnly() { + return this.builder.readOnly; + } + + @Override + public String modbus_id() { + return this.builder.modbusId; + } + + @Override + public int readFromModbusBlock() { + return this.builder.readFromModbusBlock; + } + + @Override + public String Modbus_target() { + return ConfigUtils.generateReferenceTargetFilter(this.id(), this.modbus_id()); + } + + @Override + public int modbusUnitId() { + return this.builder.modbusUnitId; + } + +} \ No newline at end of file diff --git a/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/dummy/MySunSpecComponentImpl.java b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/dummy/MySunSpecComponentImpl.java new file mode 100644 index 00000000000..119adf1cf99 --- /dev/null +++ b/io.openems.edge.bridge.modbus/test/io/openems/edge/bridge/modbus/sunspec/dummy/MySunSpecComponentImpl.java @@ -0,0 +1,77 @@ +package io.openems.edge.bridge.modbus.sunspec.dummy; + +import java.util.Map; + +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; + +import com.google.common.collect.ImmutableMap; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.bridge.modbus.api.ModbusComponent; +import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.sunspec.AbstractOpenemsSunSpecComponent; +import io.openems.edge.bridge.modbus.sunspec.DefaultSunSpecModel; +import io.openems.edge.bridge.modbus.sunspec.SunSpecModel; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.taskmanager.Priority; + +public class MySunSpecComponentImpl extends AbstractOpenemsSunSpecComponent + implements ModbusComponent, OpenemsComponent { + + private static final Map ACTIVE_MODELS = ImmutableMap.builder() + .put(DefaultSunSpecModel.S_1, Priority.LOW) // + .put(DefaultSunSpecModel.S_101, Priority.LOW) // + .put(DefaultSunSpecModel.S_103, Priority.HIGH) // + .put(DefaultSunSpecModel.S_701, Priority.HIGH) // + .put(DefaultSunSpecModel.S_702, Priority.LOW) // + .build(); + + @Reference + private ConfigurationAdmin cm; + + @Override + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + public MySunSpecComponentImpl() { + super(// + ACTIVE_MODELS, // + OpenemsComponent.ChannelId.values(), // + ModbusComponent.ChannelId.values() // + ); + } + + @Activate + private void activate(ComponentContext context, Config config) throws OpenemsException { + if (super.activate(context, config.id(), config.alias(), true, config.modbusUnitId(), this.cm, "Modbus", + config.modbus_id(), config.readFromModbusBlock())) { + return; + } + } + + @Override + public ModbusProtocol getModbusProtocol() { + return super.getModbusProtocol(); + } + + @Override + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + @Override + protected void onSunSpecInitializationCompleted() { + } + +} diff --git a/io.openems.edge.bridge.onewire/src/io/openems/edge/bridge/onewire/impl/BridgeOnewireImpl.java b/io.openems.edge.bridge.onewire/src/io/openems/edge/bridge/onewire/impl/BridgeOnewireImpl.java index 95cadfee470..b03ef071bd0 100644 --- a/io.openems.edge.bridge.onewire/src/io/openems/edge/bridge/onewire/impl/BridgeOnewireImpl.java +++ b/io.openems.edge.bridge.onewire/src/io/openems/edge/bridge/onewire/impl/BridgeOnewireImpl.java @@ -1,6 +1,5 @@ package io.openems.edge.bridge.onewire.impl; -import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import org.osgi.service.component.ComponentContext; @@ -14,16 +13,13 @@ import com.dalsemi.onewire.adapter.DSPortAdapter; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.edge.bridge.onewire.BridgeOnewire; import io.openems.edge.bridge.onewire.jsonrpc.GetDevicesRequest; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; -import io.openems.edge.common.jsonapi.JsonApi; -import io.openems.edge.common.user.User; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; @Designate(ocd = Config.class, factory = true) @Component(// @@ -34,7 +30,8 @@ @EventTopics({ // EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE // }) -public class BridgeOnewireImpl extends AbstractOpenemsComponent implements BridgeOnewire, OpenemsComponent, JsonApi { +public class BridgeOnewireImpl extends AbstractOpenemsComponent + implements BridgeOnewire, OpenemsComponent, ComponentJsonApi { private OneWireTaskWorker taskWorker = null; @@ -84,13 +81,8 @@ protected void logError(Logger log, String message) { } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest message) - throws OpenemsNamedException { - switch (message.getMethod()) { - case GetDevicesRequest.METHOD: - return CompletableFuture.completedFuture(this.taskWorker.handleGetDevicesRequest(message)); - } - return null; + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(GetDevicesRequest.METHOD, call -> this.taskWorker.handleGetDevicesRequest(call.getRequest())); } } \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/channel/calculate/CalculateAverage.java b/io.openems.edge.common/src/io/openems/edge/common/channel/calculate/CalculateAverage.java index f6e44d207cd..e6d8ef2dc0d 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/channel/calculate/CalculateAverage.java +++ b/io.openems.edge.common/src/io/openems/edge/common/channel/calculate/CalculateAverage.java @@ -26,7 +26,7 @@ public void addValue(Channel channel) { var value = channel.value().asOptional(); if (value.isPresent()) { try { - this.values.add(value.get().doubleValue()); + this.addValue(value.get()); } catch (Exception e) { this.log.error("Adding Channel [" + channel.address() + "] value [" + value + "] failed. " + e.getClass().getSimpleName() + ": " + e.getMessage()); @@ -35,6 +35,17 @@ public void addValue(Channel channel) { } } + /** + * Adds a Value. + * + * @param value the value + */ + public void addValue(Number value) { + if (value != null) { + this.values.add(value.doubleValue()); + } + } + /** * Calculates the average. * diff --git a/io.openems.edge.common/src/io/openems/edge/common/component/ComponentManager.java b/io.openems.edge.common/src/io/openems/edge/common/component/ComponentManager.java index f026cf8d3fd..ea444764b63 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/component/ComponentManager.java +++ b/io.openems.edge.common/src/io/openems/edge/common/component/ComponentManager.java @@ -7,18 +7,21 @@ import io.openems.common.channel.Level; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; +import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; +import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; import io.openems.common.types.ChannelAddress; import io.openems.common.types.EdgeConfig; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.value.Value; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.user.User; /** * A Service that provides access to OpenEMS-Components. */ -public interface ComponentManager extends OpenemsComponent, JsonApi, ClockProvider { +public interface ComponentManager extends OpenemsComponent, ClockProvider { public static final String SINGLETON_SERVICE_PID = "Core.ComponentManager"; public static final String SINGLETON_COMPONENT_ID = "_componentManager"; @@ -274,4 +277,34 @@ public default > T getChannel(ChannelAddress channelAddress */ public EdgeConfig getEdgeConfig(); + /** + * Handles a {@link CreateComponentConfigRequest}. + * + * @param user the user + * @param request the {@link CreateComponentConfigRequest} + * @throws OpenemsNamedException on error + */ + public void handleCreateComponentConfigRequest(User user, CreateComponentConfigRequest request) + throws OpenemsNamedException; + + /** + * Handles a {@link UpdateComponentConfigRequest}. + * + * @param user the user + * @param request the {@link UpdateComponentConfigRequest} + * @throws OpenemsNamedException on error + */ + public void handleUpdateComponentConfigRequest(User user, UpdateComponentConfigRequest request) + throws OpenemsNamedException; + + /** + * Handles a {@link DeleteComponentConfigRequest}. + * + * @param user the user + * @param request the {@link DeleteComponentConfigRequest} + * @throws OpenemsNamedException on error + */ + public void handleDeleteComponentConfigRequest(User user, DeleteComponentConfigRequest request) + throws OpenemsNamedException; + } \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/host/DummyHost.java b/io.openems.edge.common/src/io/openems/edge/common/host/DummyHost.java index 845c1dd479d..18378ccece7 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/host/DummyHost.java +++ b/io.openems.edge.common/src/io/openems/edge/common/host/DummyHost.java @@ -1,14 +1,8 @@ package io.openems.edge.common.host; -import java.util.concurrent.CompletableFuture; - -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.test.AbstractDummyOpenemsComponent; import io.openems.edge.common.test.TestUtils; -import io.openems.edge.common.user.User; /** * Simulates a {@link Host} for the OpenEMS Component test framework. @@ -37,10 +31,4 @@ public DummyHost withHostname(String value) { return this; } - @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException { - return null; - } - } \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/host/Host.java b/io.openems.edge.common/src/io/openems/edge/common/host/Host.java index da15ab467dc..27366c7d401 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/host/Host.java +++ b/io.openems.edge.common/src/io/openems/edge/common/host/Host.java @@ -7,9 +7,8 @@ import io.openems.edge.common.channel.StringReadChannel; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; -public interface Host extends OpenemsComponent, JsonApi { +public interface Host extends OpenemsComponent { public static final String SINGLETON_SERVICE_PID = "Core.Host"; public static final String SINGLETON_COMPONENT_ID = "_host"; diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Call.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Call.java new file mode 100644 index 00000000000..f64376644a0 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Call.java @@ -0,0 +1,102 @@ +package io.openems.edge.common.jsonapi; + +import java.util.Map; +import java.util.TreeMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Call { + + private final Logger log = LoggerFactory.getLogger(Call.class); + + private final REQUEST request; + private RESPONSE response; + private Map properties; + + private Call(REQUEST request, Map properties) { + super(); + this.request = request; + this.properties = properties; + } + + public Call(REQUEST request) { + this(request, new TreeMap<>()); + } + + public void setResponse(RESPONSE response) { + if (this.response != null) { + this.log.info("Request[" + this.request + "] was already fulfilled!"); + } + this.response = response; + } + + /** + * Gets the value to which the specific key is mapped, or null if this map + * contains no mapping for the key. + * + *

+ * The properties may contain special information about the current {@link Call} + * e. g. which user did trigger this call. + * + * @param the type of value + * @param key the key whose associated value is to be returned + * @return the value to which the specified key is mapped, or null if this map + * contains no mapping for the key + * @see Call#put(Key, Object) + */ + @SuppressWarnings("unchecked") + public T get(Key key) { + return (T) this.properties.get(key.identifier()); + } + + /** + * Associates the specified value with the specified key in this map. If the map + * previously contained a mapping for the key, the old value is replaced by the + * specified value. + * + *

+ * With this values can be associated to the current call e. g. the user who + * triggered this call. + * + * @param the type of the value + * @param key key with which the specified value is to be associated + * @param value value to be associated with the specified key + */ + public void put(Key key, T value) { + this.properties.put(key.identifier(), value); + } + + /** + * Creates a new {@link Call} with the given request and applies all properties + * to the new {@link Call}. + * + * @param the type of the new request + * @param request the new request + * @return the new {@link Call} + */ + public Call mapRequest(REQ request) { + return new Call<>(request, this.properties); + } + + /** + * Creates a new {@link Call} with the type of the response mapped to a new + * type. The current request and the properties are applied to the new + * {@link Call}. + * + * @param the new type of the response + * @return the new {@link Call} + */ + public Call mapResponse() { + return new Call<>(this.request, this.properties); + } + + public REQUEST getRequest() { + return this.request; + } + + public RESPONSE getResponse() { + return this.response; + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/ComponentJsonApi.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/ComponentJsonApi.java new file mode 100644 index 00000000000..e12c09553fa --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/ComponentJsonApi.java @@ -0,0 +1,12 @@ +package io.openems.edge.common.jsonapi; + +public interface ComponentJsonApi extends JsonApi { + + /** + * Returns a unique ID for this OpenEMS component. + * + * @return the unique ID + */ + public String id(); + +} diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EdgeGuards.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EdgeGuards.java new file mode 100644 index 00000000000..43ce40ec96d --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EdgeGuards.java @@ -0,0 +1,46 @@ +package io.openems.edge.common.jsonapi; + +import io.openems.common.session.Role; +import io.openems.edge.common.user.User; + +public final class EdgeGuards { + + /** + * Creates a {@link JsonrpcEndpointGuard} which checks if the {@link Role} of + * the current {@link User} is atleast the given {@link Role}. + * + * @param role the role which the user should atleast have + * @return the created {@link JsonrpcEndpointGuard} + */ + public static JsonrpcEndpointGuard roleIsAtleast(Role role) { + return new JsonrpcRoleEndpointGuard(role); + } + + /** + * Creates a {@link JsonrpcEndpointGuard} which checks if the request is NOT + * from the backend and if so checks if the {@link Role} of the current + * {@link User} is atleast the given {@link Role}. + * + * @param role the role which the user should atleast have + * @return the created {@link JsonrpcEndpointGuard} + */ + public static JsonrpcEndpointGuard roleIsAtleastNotFromBackend(Role role) { + return new JsonrpcBackendRoleEndpointGuard(role, false); + } + + /** + * Creates a {@link JsonrpcEndpointGuard} which checks if the request is from + * the backend and if so checks if the {@link Role} of the current {@link User} + * is atleast the given {@link Role}. + * + * @param role the role which the user should atleast have + * @return the created {@link JsonrpcEndpointGuard} + */ + public static JsonrpcEndpointGuard roleIsAtleastFromBackend(Role role) { + return new JsonrpcBackendRoleEndpointGuard(role, true); + } + + private EdgeGuards() { + } + +} diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EdgeKeys.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EdgeKeys.java new file mode 100644 index 00000000000..cbfb6ef940d --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EdgeKeys.java @@ -0,0 +1,14 @@ +package io.openems.edge.common.jsonapi; + +import io.openems.edge.common.user.User; + +public final class EdgeKeys { + + public static final Key USER_KEY = new Key<>("user", User.class); + + public static final Key IS_FROM_BACKEND_KEY = new Key<>("isFromBackend", Boolean.class); + + private EdgeKeys() { + } + +} diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointDefinitionBuilder.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointDefinitionBuilder.java new file mode 100644 index 00000000000..e1072f534a0 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointDefinitionBuilder.java @@ -0,0 +1,133 @@ +package io.openems.edge.common.jsonapi; + +import static java.util.Collections.emptyList; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +import io.openems.common.jsonrpc.serialization.JsonSerializer; + +public final class EndpointDefinitionBuilder { + private final EndpointRequestDefinitionBuilder endpointRequestBuilder = new EndpointRequestDefinitionBuilder<>(); + private final EndpointResponseDefinitionBuilder endpointResponseBuilder = new EndpointResponseDefinitionBuilder<>(); + private final List tags = new ArrayList<>(); + private String description; + private List guards = emptyList(); + + /** + * Sets the description for the current endpoint. + * + * @param description the description of the endpoint + * @return this + */ + public EndpointDefinitionBuilder setDescription(String description) { + this.description = description; + return this; + } + + /** + * Sets the {@link JsonrpcEndpointGuard JsonrpcEndpointGuards} for the current + * endpoint. + * + * @param guards the {@link JsonrpcEndpointGuard JsonrpcEndpointGuards} of the + * endpoint + * @return this + */ + public EndpointDefinitionBuilder setGuards(JsonrpcEndpointGuard... guards) { + this.guards = List.of(guards); + return this; + } + + /** + * Sets the {@link JsonSerializer} of the request. + * + * @param serializer the request {@link JsonSerializer} + * @return this + */ + public EndpointDefinitionBuilder setRequestSerializer(// + JsonSerializer serializer // + ) { + this.endpointRequestBuilder.setSerializer(serializer); + return this; + } + + /** + * Applies the request builder configuration of the consumer to the current + * request definition. + * + * @param builder the builder consumer + * @return this + */ + public EndpointDefinitionBuilder applyRequestBuilder(// + Consumer> builder // + ) { + builder.accept(this.endpointRequestBuilder); + return this; + } + + /** + * Sets the {@link JsonSerializer} of the request and applies the request + * builder configuration of the consumer to the current request definition. + * + * @param serializer the request {@link JsonSerializer} + * @param builder the builder consumer + * @return this + */ + public EndpointDefinitionBuilder applyRequestBuilderWithSerializer(// + JsonSerializer serializer, // + Consumer> builder // + ) { + this.setRequestSerializer(serializer); + this.applyRequestBuilder(builder); + return this; + } + + /** + * Sets the {@link JsonSerializer} of the response. + * + * @param serializer the response {@link JsonSerializer} + * @return this + */ + public EndpointDefinitionBuilder setResponseSerializer(// + JsonSerializer serializer // + ) { + this.endpointResponseBuilder.setSerializer(serializer); + return this; + } + + /** + * Applies the response builder configuration of the consumer to the current + * response definition. + * + * @param builder the builder consumer + * @return this + */ + public EndpointDefinitionBuilder applyResponseBuilder(// + Consumer> builder // + ) { + builder.accept(this.endpointResponseBuilder); + return this; + } + + public String getDescription() { + return this.description; + } + + public List getGuards() { + return this.guards; + } + + public EndpointRequestDefinitionBuilder getEndpointRequestBuilder() { + return this.endpointRequestBuilder; + } + + public EndpointResponseDefinitionBuilder getEndpointResponseBuilder() { + return this.endpointResponseBuilder; + } + + public List getTags() { + return this.tags; + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointRequestDefinitionBuilder.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointRequestDefinitionBuilder.java new file mode 100644 index 00000000000..cde58c31b0b --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointRequestDefinitionBuilder.java @@ -0,0 +1,78 @@ +package io.openems.edge.common.jsonapi; + +import static io.openems.common.utils.JsonUtils.toJsonArray; + +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.JsonArray; + +import io.openems.common.jsonrpc.serialization.JsonSerializer; +import io.openems.common.utils.JsonUtils; + +public final class EndpointRequestDefinitionBuilder { + + private JsonSerializer serializer; + private final List> examples = new ArrayList<>(); + + /** + * Sets the {@link JsonSerializer} of the request. + * + * @param serializer the request {@link JsonSerializer} + * @return this + */ + public EndpointRequestDefinitionBuilder setSerializer(// + JsonSerializer serializer // + ) { + this.serializer = serializer; + return this; + } + + /** + * Adds an example object to the current request definition associated to the + * given name. + * + * @param name the name of the example + * @param request the example object + * @return this + */ + public EndpointRequestDefinitionBuilder addExample(String name, REQUEST request) { + this.examples.add(new Example<>(name, request)); + return this; + } + + /** + * Adds an example object to the current request definition. + * + * @param request the example object + * @return this + */ + public EndpointRequestDefinitionBuilder addExample(REQUEST request) { + this.examples.add(new Example<>("<" + this.examples.size() + ">", request)); + return this; + } + + public JsonSerializer getSerializer() { + return this.serializer; + } + + public List> getExamples() { + return this.examples; + } + + /** + * Creates a {@link JsonArray} from the examples of the current request + * definition. + * + * @return the {@link JsonArray} with the examples + */ + public JsonArray createExampleArray() { + return this.examples.stream() // + .map(t -> JsonUtils.buildJsonObject() // + .addProperty("key", t.identifier()) // + .add("value", this.serializer.serialize(t.exampleObject())) // + .build()) // + .collect(toJsonArray()); + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointRequestType.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointRequestType.java new file mode 100644 index 00000000000..21b1085395a --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointRequestType.java @@ -0,0 +1,30 @@ +package io.openems.edge.common.jsonapi; + +import io.openems.common.jsonrpc.serialization.JsonSerializer; + +public interface EndpointRequestType { + + /** + * Gets the name of the request method. + * + * @return the method name + */ + public abstract String getMethod(); + + /** + * Gets the {@link JsonSerializer} for the request. Used to convert the received + * json into a java object. + * + * @return the request {@link JsonSerializer} + */ + public abstract JsonSerializer getRequestSerializer(); + + /** + * Gets the {@link JsonSerializer} for the response. Used to convert the + * response java object into a json string. + * + * @return the response {@link JsonSerializer} + */ + public abstract JsonSerializer getResponseSerializer(); + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointResponseDefinitionBuilder.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointResponseDefinitionBuilder.java new file mode 100644 index 00000000000..fc5a03a3389 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/EndpointResponseDefinitionBuilder.java @@ -0,0 +1,83 @@ +package io.openems.edge.common.jsonapi; + +import static io.openems.common.utils.JsonUtils.toJsonArray; + +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.JsonArray; + +import io.openems.common.jsonrpc.serialization.JsonSerializer; +import io.openems.common.utils.JsonUtils; + +public final class EndpointResponseDefinitionBuilder { + + private JsonSerializer serializer; + private final List> examples = new ArrayList<>(); + + /** + * Sets the {@link JsonSerializer} of the response. + * + * @param serializer the response {@link JsonSerializer} + * @return this + */ + public EndpointResponseDefinitionBuilder setSerializer(// + JsonSerializer serializer // + ) { + this.serializer = serializer; + return this; + } + + /** + * Adds an example object to the current response definition associated to the + * given name. + * + * @param name the name of the example + * @param response the example object + * @return this + */ + public EndpointResponseDefinitionBuilder addExample(// + String name, // + RESPONSE response // + ) { + this.examples.add(new Example<>(name, response)); + return this; + } + + /** + * Adds an example object to the current request definition. + * + * @param response the example object + * @return this + */ + public EndpointResponseDefinitionBuilder addExample(// + RESPONSE response // + ) { + this.examples.add(new Example<>("<" + this.examples.size() + ">", response)); + return this; + } + + public JsonSerializer getSerializer() { + return this.serializer; + } + + public List> getExamples() { + return this.examples; + } + + /** + * Creates a {@link JsonArray} from the examples of the current request + * definition. + * + * @return the {@link JsonArray} with the examples + */ + public JsonArray createExampleArray() { + return this.examples.stream() // + .map(t -> JsonUtils.buildJsonObject() // + .addProperty("key", t.identifier()) // + .add("value", this.serializer.serialize(t.exampleObject())) // + .build()) // + .collect(toJsonArray()); + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Example.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Example.java new file mode 100644 index 00000000000..9ea4f2bd7bc --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Example.java @@ -0,0 +1,5 @@ +package io.openems.edge.common.jsonapi; + +public record Example(String identifier, T exampleObject) { + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApi.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApi.java index 370dbae51d9..1b78a9b7ba5 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApi.java +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApi.java @@ -1,27 +1,27 @@ package io.openems.edge.common.jsonapi; -import java.util.concurrent.CompletableFuture; - -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; -import io.openems.edge.common.user.User; - /** * Declares a class as being able to handle JSON-RPC Requests. */ public interface JsonApi { /** - * Handles a JSON-RPC Request. - * - * @param user the authenticated {@link User} - * @param request the JSON-RPC Request - * @return a Future JSON-RPC Success Response; null response results in a - * OpenemsError.JSONRPC_UNHANDLED_METHOD - * @throws OpenemsNamedException on error + * Specifies routes of the current component in the given builder. + * + *

+ * Example:
+ * + *

+	 * {@code @Override}
+	 * public void buildJsonApiRoutes(JsonApiBuilder builder) {
+	 *     builder.rpc("METHOD_NAME", call -> {
+	 *        // handle call...
+	 *     });
+	 * }
+	 * 
+ * + * @param builder the builder to add the routes */ - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException; + public void buildJsonApiRoutes(JsonApiBuilder builder); -} +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApiBuilder.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApiBuilder.java new file mode 100644 index 00000000000..e1c1cae8031 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApiBuilder.java @@ -0,0 +1,609 @@ +package io.openems.edge.common.jsonapi; + +import static java.util.stream.Collectors.joining; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.IntStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableMap; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.function.ThrowingConsumer; +import io.openems.common.function.ThrowingFunction; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; +import io.openems.common.jsonrpc.base.JsonrpcResponseError; +import io.openems.common.jsonrpc.serialization.JsonSerializer; +import io.openems.common.utils.FunctionUtils; + +/** + * TODO route interceptor/observer/notifications. + */ +public class JsonApiBuilder { + + private final Logger log = LoggerFactory.getLogger(JsonApiBuilder.class); + + private boolean debug = false; + + private final Map endpoints = new TreeMap<>(); + + private final List builder = new ArrayList<>(); + private final List> endpointAddedListeners = new ArrayList<>(); + private final List> endpointRemovedListeners = new ArrayList<>(); + + private final Consumer endpointAddedListener = e -> { + this.endpoints.put(e.getMethod(), e); + }; + + private final Consumer endpointRemovedListener = e -> { + this.endpoints.remove(e.getMethod()); + }; + + /** + * Adds a rpc request handler to the current builder. + * + * @param method the method of the handled rpc request + * @param handler the handler of the request + * @return this + */ + public JsonApiBuilder handleRequest(// + final String method, // + final ThrowingFunction, JsonrpcResponse, Exception> handler // + ) { + return this.rpc(method, call -> { + call.setResponse(handler.apply(call)); + }); + } + + /** + * Adds a rpc request handler to the current builder. + * + * @param method the method of the handled rpc request + * @param defBuilder the builder for the {@link EndpointDefinitionBuilder} + * @param handler the handler of the request + * @return this + */ + public JsonApiBuilder handleRequest(// + final String method, // + final Consumer> defBuilder, // + final ThrowingFunction, JsonrpcResponse, Exception> handler // + ) { + return this.rpc(method, defBuilder, call -> { + call.setResponse(handler.apply(call)); + }); + } + + /** + * Adds a rpc request handler to the current builder. + * + * @param the type of the request + * @param the type of the response + * @param endpointType the {@link EndpointRequestType} of the handled rpc + * request + * @param defBuilder the builder for the {@link EndpointDefinitionBuilder} + * @param handler the handler of the request + * @return this + */ + public JsonApiBuilder handleRequest(// + final EndpointRequestType endpointType, // + final Consumer> defBuilder, // + final ThrowingFunction, RESPONSE, Exception> handler // + ) { + return this.rpc(endpointType.getMethod(), defBuilder, call -> { + call.setResponse(handler.apply(call)); + }, endpointType.getRequestSerializer(), endpointType.getResponseSerializer()); + } + + /** + * Adds a rpc handler to the current builder. + * + * @param method the method of the handled rpc request + * @param handler the handler of the request + * @return this + */ + public JsonApiBuilder rpc(// + final String method, // + final ThrowingConsumer, Exception> handler // + ) { + this.addEndpoint(new JsonApiEndpoint(method, (b, call) -> { + try { + handler.accept(call); + } catch (Exception e) { + call.setResponse(this.handleException(call, e)); + } + })); + return this; + } + + /** + * Adds a rpc handler to the current builder. + * + * @param method the method of the handled rpc request + * @param defBuilder the builder for the {@link EndpointDefinitionBuilder} + * @param handler the handler of the request + * @return this + */ + public JsonApiBuilder rpc(// + final String method, // + final Consumer> defBuilder, // + final ThrowingConsumer, Exception> handler // + ) { + final var endpointDef = new EndpointDefinitionBuilder(); + defBuilder.accept(endpointDef); + this.addEndpoint(new JsonApiEndpoint(method, (b, call) -> { + try { + for (var guard : endpointDef.getGuards()) { + guard.test(call); + } + handler.accept(call); + } catch (Exception e) { + call.setResponse(this.handleException(call, e)); + } + }, endpointDef)); + return this; + } + + /** + * Adds a rpc handler to the current builder. + * + * @param the type of the request + * @param the type of the response + * @param method the method of the handled rpc request + * @param defBuilder the builder for the + * {@link EndpointDefinitionBuilder} + * @param handler the handler of the request + * @param requestSerializer the {@link JsonSerializer} of the request + * @param responseSerializer the {@link JsonSerializer} of the response + * @return this + */ + public JsonApiBuilder rpc(// + final String method, // + final Consumer> defBuilder, // + final ThrowingConsumer, Exception> handler, // + final JsonSerializer requestSerializer, // + final JsonSerializer responseSerializer // + ) { + final var endpointDef = new EndpointDefinitionBuilder(); + endpointDef.setRequestSerializer(requestSerializer); + endpointDef.setResponseSerializer(responseSerializer); + defBuilder.accept(endpointDef); + + this.addEndpoint(new JsonApiEndpoint(method, (b, t) -> { + for (var guard : endpointDef.getGuards()) { + try { + guard.test(t); + } catch (Exception e) { + t.setResponse(this.handleException(t, e)); + return; + } + } + try { + final var mappedCall = t.mapRequest(requestSerializer.deserialize(t.getRequest().getParams())) // + .mapResponse(); + + handler.accept(mappedCall); + + t.setResponse(new GenericJsonrpcResponseSuccess(t.getRequest().getId(), + responseSerializer.serialize(mappedCall.getResponse()).getAsJsonObject())); + } catch (Exception e) { + t.setResponse(this.handleException(t, e)); + } + }, endpointDef)); + return this; + } + + /** + * Adds a rpc handler to the current builder. + * + * @param the type of the request + * @param method the method of the handled rpc request + * @param defBuilder the builder for the {@link EndpointDefinitionBuilder} + * @param subroutes the subroutes which can be reached with this handler + * @param handler the handler of the request + * @param serializer the {@link JsonSerializer} of the request + * @return this + */ + public JsonApiBuilder rpc(// + final String method, // + final Consumer> defBuilder, // + final Supplier> subroutes, // + final ThrowingConsumer, Exception> handler, // + final JsonSerializer serializer // + ) { + final var endpointDef = new EndpointDefinitionBuilder(); + endpointDef.setRequestSerializer(serializer); + defBuilder.accept(endpointDef); + + this.addEndpoint(new JsonApiEndpoint(method, (b, t) -> { + try { + for (var guard : endpointDef.getGuards()) { + guard.test(t); + } + + var call = t.mapRequest(serializer.deserialize(t.getRequest().getParams())); + handler.accept(call); + if (call.getResponse() != null) { + t.setResponse(call.getResponse()); + } + } catch (Exception e) { + t.setResponse(this.handleException(t, e)); + } + }, endpointDef, subroutes)); + return this; + } + + /** + * Adds a rpc handler to the current builder. + * + * @param method the method of the handled rpc request + * @param defBuilder the builder for the {@link EndpointDefinitionBuilder} + * @param subroutes the subroutes which can be reached with this handler + * @param handler the handler of the request + * @return this + */ + public JsonApiBuilder rpc(// + final String method, // + final Consumer> defBuilder, // + final Supplier> subroutes, // + final ThrowingConsumer, Exception> handler // + ) { + final var endpointDef = new EndpointDefinitionBuilder(); + defBuilder.accept(endpointDef); + + this.addEndpoint(new JsonApiEndpoint(method, (b, t) -> { + try { + for (var guard : endpointDef.getGuards()) { + guard.test(t); + } + + handler.accept(t); + } catch (Exception e) { + t.setResponse(this.handleException(t, e)); + } + }, endpointDef, subroutes)); + return this; + } + + /** + * Delegates the handled request to another endpoint. + * + * @param method the method of the handled rpc request + * @param defBuilder the builder for the {@link EndpointDefinitionBuilder} + * @param handler the handler of the request which returns the delegated + * request + * @param builder the path to the builder which handles the delegated + * request + * @param responseMapper the mapper of the response + * @param subroutes the subroutes which can be reached with this handler + * @return this + */ + public JsonApiBuilder delegate(// + final String method, // + final Consumer> defBuilder, // + final ThrowingFunction, JsonrpcRequest, Exception> handler, // + final Function builder, // + final Function responseMapper, // + final Supplier> subroutes // + ) { + final var endpointDef = new EndpointDefinitionBuilder(); + defBuilder.accept(endpointDef); + this.addEndpoint(new JsonApiEndpoint(method, (b, t) -> { + try { + final var call = t.mapRequest(handler.apply(t)); + builder.apply(b).handle(call); + if (call.getResponse() != null) { + t.setResponse(responseMapper.apply(call.getResponse())); + } + } catch (Exception e) { + this.handleException(t, e); + } + }, endpointDef, subroutes)); + return this; + } + + /** + * Delegates the handled request to another endpoint. + * + * @param method the method of the handled rpc request + * @param handler the handler of the request which returns the delegated + * request + * @param builder the path to the builder which handles the delegated request + * @param subroutes the subroutes which can be reached with this handler + * @return this + */ + public JsonApiBuilder delegate(// + final String method, // + final ThrowingFunction, JsonrpcRequest, Exception> handler, // + final Function builder, // + final Supplier> subroutes // + ) { + return this.delegate(method, FunctionUtils::doNothing, handler, builder, Function.identity(), subroutes); + } + + /** + * Delegates the handled request to another endpoint. + * + * @param method the method of the handled rpc request + * @param defBuilder the builder for the {@link EndpointDefinitionBuilder} + * @param handler the handler of the request which returns the delegated + * request + * @param builder the path to the builder which handles the delegated request + * @return this + */ + public JsonApiBuilder delegate(// + final String method, // + final Consumer> defBuilder, // + final ThrowingFunction, JsonrpcRequest, Exception> handler, // + final Function builder // + ) { + return this.delegate(method, defBuilder, handler, builder, Function.identity(), null); + } + + /** + * Delegates the handled request to another endpoint. + * + * @param method the method of the handled rpc request + * @param handler the handler of the request which returns the delegated request + * @param builder the path to the builder which handles the delegated request + * @return this + */ + public JsonApiBuilder delegate(// + final String method, // + final ThrowingFunction, JsonrpcRequest, Exception> handler, // + final Function builder // + ) { + return this.delegate(method, FunctionUtils::doNothing, handler, builder, Function.identity(), null); + } + + /** + * Delegates the handled request to another endpoint. + * + * @param method the method of the handled rpc request + * @param defBuilder the builder for the {@link EndpointDefinitionBuilder} + * @param handler the handler of the request which returns the delegated + * request + * @return this + */ + public JsonApiBuilder delegate(// + final String method, // + final Consumer> defBuilder, // + final ThrowingFunction, JsonrpcRequest, Exception> handler // + ) { + return this.delegate(method, defBuilder, handler, Function.identity()); + } + + /** + * Delegates the handled request to another endpoint. + * + * @param method the method of the handled rpc request + * @param handler the handler of the request which returns the delegated request + * @return this + */ + public JsonApiBuilder delegate(// + final String method, // + final ThrowingFunction, JsonrpcRequest, Exception> handler // + ) { + return this.delegate(method, FunctionUtils::doNothing, handler, Function.identity()); + } + + private void addEndpoint(JsonApiEndpoint endpoint) { + synchronized (this.endpoints) { + final var previous = this.endpoints.put(endpoint.getMethod(), endpoint); + if (previous != null) { + this.endpointRemovedListeners.forEach(t -> t.accept(previous)); + this.log.error("Duplicated endpoint defined for method '" + endpoint.getMethod() + + "'. Override with last defined endpoint"); + } + this.endpointAddedListeners.forEach(t -> t.accept(endpoint)); + + if (this.isDebug()) { + this.log.info("Added handler for method '" + endpoint.getMethod() + "'"); + } + } + } + + /** + * Removes an endpoint by its method. + * + * @param method the method of the endpoint to remove + * @return the endpoint which got removed or null if no endpoint with this + * method exists + */ + public JsonApiEndpoint removeEndpoint(String method) { + synchronized (this.endpoints) { + final var removedEndpoint = this.endpoints.remove(method); + if (removedEndpoint == null) { + return null; + } + this.endpointRemovedListeners.forEach(t -> t.accept(removedEndpoint)); + + if (this.isDebug()) { + this.log.info("Removed handler for method '" + method + "'"); + } + return removedEndpoint; + } + } + + /** + * Adds a {@link JsonApiBuilder} to the current builder. All methods are + * "copied" to this builder and available for both then. + * + * @param builder the {@link JsonApiBuilder} to add + * @see JsonApiBuilder#removeBuilder(JsonApiBuilder) + */ + public void addBuilder(JsonApiBuilder builder) { + synchronized (builder.endpoints) { + this.builder.add(builder); + builder.addEndpointAddedListener(this.endpointAddedListener); + builder.addEndpointRemovedListener(this.endpointRemovedListener); + builder.getEndpoints().forEach((t, u) -> this.addEndpoint(u)); + } + } + + /** + * Removes a {@link JsonApiBuilder} and all its methods from this builder. + * + * @param builder the {@link JsonApiBuilder} to remove + * @see JsonApiBuilder#addBuilder(JsonApiBuilder) + */ + public void removeBuilder(JsonApiBuilder builder) { + synchronized (builder.endpoints) { + this.builder.remove(builder); + builder.removeEndpointAddedListener(this.endpointAddedListener); + builder.removeEndpointRemovedListener(this.endpointRemovedListener); + builder.getEndpoints().forEach((t, u) -> this.removeEndpoint(t)); + } + } + + /** + * Adds a listener to call when a endpoint got added to the current builder. + * + * @param listener the listener to call when the event happened + */ + public void addEndpointAddedListener(Consumer listener) { + this.endpointAddedListeners.add(listener); + } + + /** + * Removes a listener which was subscribed to the endpoint added event. + * + * @param listener the listener to remove + */ + public void removeEndpointAddedListener(Consumer listener) { + this.endpointAddedListeners.remove(listener); + } + + /** + * Adds a listener to call when a endpoint got removed to the current builder. + * + * @param listener the listener to call when the event happened + */ + public void addEndpointRemovedListener(Consumer listener) { + this.endpointRemovedListeners.add(listener); + } + + /** + * Removes a listener which was subscribed to the endpoint removed event. + * + * @param listener the listener to remove + */ + public void removeEndpointRemovedListener(Consumer listener) { + this.endpointRemovedListeners.remove(listener); + } + + public Map getEndpoints() { + return ImmutableMap.copyOf(this.endpoints); + } + + public boolean isDebug() { + return this.debug; + } + + public void setDebug(boolean debug) { + this.debug = debug; + } + + /** + * Closes this builder. + * + *

+ * Removes all {@link JsonApiBuilder} from this builder. + */ + public void close() { + final var builderToRemove = new ArrayList<>(this.builder); + builderToRemove.forEach(this::removeBuilder); + } + + private JsonrpcResponseError handleException(Call call, Throwable t) { + if (this.isDebug()) { + this.log.error(t.getMessage(), t); + } + + // Get JSON-RPC Response Error + if (t instanceof OpenemsNamedException ex) { + return new JsonrpcResponseError(call.getRequest().getId(), ex); + } else { + return new JsonrpcResponseError(call.getRequest().getId(), t.getMessage()); + } + } + + private static final Key DEPTH = new Key("depth", Integer.class); + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static final Key> TRACE = (Key>) new Key("trace", List.class); + + private static record JsonHandlerTrace(// + String method, // + boolean exists // + ) { + + } + + /** + * Handles a {@link Call}. + * + *

+ * A {@link Call} is usually created and passed to a builder when a request from + * an user is called in the UI or via code directly (BackendController, + * WebsocketController, RestApi, ...). + * + * @param call the call to handle + */ + public void handle(Call call) { + var depth = call.get(DEPTH); + final var trace = call.get(TRACE) != null ? call.get(TRACE) : new ArrayList(); + this.handle(// + depth == null ? 0 : depth, // + trace, // + call // + ); + + if (depth != null) { + return; + } + + if (!this.isDebug()) { + return; + } + this.log.info("Debug info for Request " + call.getRequest().getId() + "" + System.lineSeparator() // + + IntStream.range(0, trace.size()) // + .mapToObj(i -> { + var t = trace.get(i); + return Strings.repeat(" ", i) + "\\ " + t.method() + (t.exists() ? "" : " (missing)"); + }).collect(joining(System.lineSeparator())) // + + System.lineSeparator() + Strings.repeat(" ", trace.size()) + " -> "// + + (call.getResponse() == null ? "Missing Response" : call.getResponse().toJsonObject())); + } + + private void handle(// + final int depth, // + final List trace, // + Call call // + ) { + final var endpoint = this.endpoints.get(call.getRequest().getMethod()); + trace.add(new JsonHandlerTrace(call.getRequest().getMethod(), endpoint != null)); + call.put(DEPTH, depth + 1); + call.put(TRACE, trace); + + if (endpoint == null) { + // TODO later for notifications there should not be a response + call.setResponse(new JsonrpcResponseError(call.getRequest().getId(), + "Endpoint with method \"" + call.getRequest().getMethod() + "\" is not defined!")); + return; + } + + // handle request + endpoint.handle(this, call); + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApiEndpoint.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApiEndpoint.java new file mode 100644 index 00000000000..aa3307423e9 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonApiEndpoint.java @@ -0,0 +1,66 @@ +package io.openems.edge.common.jsonapi; + +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; + +public class JsonApiEndpoint { + private final String method; + private final BiConsumer> handler; + private final EndpointDefinitionBuilder def; + private final Supplier> subroutes; + + public JsonApiEndpoint(// + String method, // + BiConsumer> handler, // + EndpointDefinitionBuilder def, // + Supplier> subroutes // + ) { + super(); + this.method = method; + this.handler = handler; + this.def = def; + this.subroutes = subroutes; + } + + public JsonApiEndpoint(// + String method, // + BiConsumer> handler, // + EndpointDefinitionBuilder def // + ) { + this(method, handler, def, null); + } + + public JsonApiEndpoint(// + String method, // + BiConsumer> handler // + ) { + this(method, handler, new EndpointDefinitionBuilder()); + } + + public String getMethod() { + return this.method; + } + + public Supplier> getSubroutes() { + return this.subroutes; + } + + public EndpointDefinitionBuilder getDef() { + return this.def; + } + + /** + * Handles the call with the current endpoint handler. + * + * @param builder the current root {@link JsonApiBuilder} + * @param call the {@link Call} to handle + */ + public void handle(JsonApiBuilder builder, Call call) { + this.handler.accept(builder, call); + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonrpcBackendRoleEndpointGuard.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonrpcBackendRoleEndpointGuard.java new file mode 100644 index 00000000000..dc13f85a541 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonrpcBackendRoleEndpointGuard.java @@ -0,0 +1,28 @@ +package io.openems.edge.common.jsonapi; + +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; +import io.openems.common.session.Role; + +public final class JsonrpcBackendRoleEndpointGuard implements JsonrpcEndpointGuard { + + private final JsonrpcEndpointGuard roleGuard; + private final boolean checkIfBackend; + + public JsonrpcBackendRoleEndpointGuard(Role role, boolean checkIfBackend) { + this.roleGuard = EdgeGuards.roleIsAtleast(role); + this.checkIfBackend = checkIfBackend; + } + + @Override + public void test(Call call) throws Exception { + final var isFromBackend = call.get(EdgeKeys.IS_FROM_BACKEND_KEY); + + if ((isFromBackend != null && isFromBackend) != this.checkIfBackend) { + return; + } + + this.roleGuard.test(call); + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonrpcEndpointGuard.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonrpcEndpointGuard.java new file mode 100644 index 00000000000..128a5d150da --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonrpcEndpointGuard.java @@ -0,0 +1,17 @@ +package io.openems.edge.common.jsonapi; + +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; + +public interface JsonrpcEndpointGuard { + + /** + * Checks if the current call fulfills the required check. If not an + * {@link Exception} gets thrown. + * + * @param call the call to check + * @throws Exception on error + */ + public void test(Call call) throws Exception; + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonrpcRoleEndpointGuard.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonrpcRoleEndpointGuard.java new file mode 100644 index 00000000000..a21d6df0994 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/JsonrpcRoleEndpointGuard.java @@ -0,0 +1,44 @@ +package io.openems.edge.common.jsonapi; + +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; + +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; +import io.openems.common.jsonrpc.serialization.JsonSerializer; +import io.openems.common.session.Role; +import io.openems.common.utils.JsonUtils; + +public final class JsonrpcRoleEndpointGuard implements JsonrpcEndpointGuard { + + private final Role role; + + public JsonrpcRoleEndpointGuard(Role role) { + this.role = role; + } + + @Override + public void test(Call call) throws Exception { + call.get(EdgeKeys.USER_KEY).getRole().assertIsAtLeast(call.getRequest().getMethod(), this.role); + } + + public Role getRole() { + return this.role; + } + + /** + * Returns a {@link JsonSerializer} for a {@link JsonrpcRoleEndpointGuard}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(JsonrpcRoleEndpointGuard.class, + t -> new JsonrpcRoleEndpointGuard(Role.getRole(t.getStringPath("role") // + .get())), + t -> JsonUtils.buildJsonObject() // + .addProperty("name", "role") // + .addProperty("description", "User-Role has to be at least " + t.getRole()) // + .addProperty("role", t.getRole()) // + .build()); + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Key.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Key.java new file mode 100644 index 00000000000..1f3d4e877dc --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Key.java @@ -0,0 +1,5 @@ +package io.openems.edge.common.jsonapi; + +public record Key(String identifier, Class type) { + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/MultipleJsonApiBinder.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/MultipleJsonApiBinder.java new file mode 100644 index 00000000000..f051eaa18eb --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/MultipleJsonApiBinder.java @@ -0,0 +1,78 @@ +package io.openems.edge.common.jsonapi; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MultipleJsonApiBinder { + + private final Logger log = LoggerFactory.getLogger(MultipleJsonApiBinder.class); + + private final JsonApiBuilder jsonApiBuilder = new JsonApiBuilder(); + private final Map handler = new HashMap<>(); + + /** + * Binds the new handler and adds all its route paths. If the handler was + * already binded replaces the old {@link JsonApiBuilder} with a new one and + * logs a warning. + * + *

+ * Commonly used like this with OSGi injection to bind all {@link JsonApi} + * which target the specific {@code ENTRY_POINT}:
+ * + *

+	 * {@code @Reference}(//
+	 *      target = "(entry=" + ENTRY_POINT + ")", //
+	 *      policyOption = ReferencePolicyOption.GREEDY, //
+	 *      policy = ReferencePolicy.DYNAMIC, //
+	 *      cardinality = ReferenceCardinality.MULTIPLE //
+	 * )
+	 * protected void bindHandler(JsonApi2 handler) {
+	 *    this.binder.bindJsonApi(handler);
+	 * }
+	 * 
+ * + * @param handler the handler to bind + * @see MultipleJsonApiBinder#unbindJsonApi(JsonApi) + */ + public void bindJsonApi(JsonApi handler) { + final var newBuilder = new JsonApiBuilder(); + final var prevBuilder = this.handler.put(handler, newBuilder); + if (prevBuilder != null) { + this.log.warn("Builder for handler " + handler + " was already existing."); + } + handler.buildJsonApiRoutes(newBuilder); + this.jsonApiBuilder.addBuilder(newBuilder); + } + + /** + * Unbinds a handler and removes all its route paths. + * + *

+ * Commonly used like this with OSGi injection to unbind a {@link JsonApi}:
+ * + *

+	 * protected void unbindHandler(JsonApi2 handler) {
+	 * 	this.binder.unbindJsonApi(handler);
+	 * }
+	 * 
+ * + * @param handler the {@link JsonApi} to remove + * @see MultipleJsonApiBinder#bindJsonApi(JsonApi) + */ + public void unbindJsonApi(JsonApi handler) { + final var builder = this.handler.remove(handler); + if (builder == null) { + return; + } + builder.close(); + this.jsonApiBuilder.removeBuilder(builder); + } + + public JsonApiBuilder getJsonApiBuilder() { + return this.jsonApiBuilder; + } + +} diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/SingleJsonApiBinder.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/SingleJsonApiBinder.java new file mode 100644 index 00000000000..e13e2838e9e --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/SingleJsonApiBinder.java @@ -0,0 +1,102 @@ +package io.openems.edge.common.jsonapi; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; +import io.openems.common.jsonrpc.base.JsonrpcResponseError; +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.utils.FunctionUtils; + +public class SingleJsonApiBinder { + + private boolean debug = false; + + private JsonApi jsonApi; + private JsonApiBuilder builder; + + /** + * Binds the {@link JsonApi} as the current handler. + * + * @param jsonApi the {@link JsonApi} to bind + * @return the created {@link JsonApiBuilder} from the {@link JsonApi} + */ + public JsonApiBuilder bind(JsonApi jsonApi) { + this.jsonApi = jsonApi; + this.builder = new JsonApiBuilder(); + this.builder.setDebug(this.debug); + this.jsonApi.buildJsonApiRoutes(this.builder); + return this.builder; + } + + /** + * Unbinds the current active {@link JsonApi} and its created + * {@link JsonApiBuilder}. + */ + public void unbind() { + this.jsonApi = null; + this.builder.close(); + this.builder = null; + } + + /** + * Handles a {@link JsonrpcRequest}. + * + * @param request the request to handle + * @return the result future + * @throws OpenemsNamedException on error + */ + public CompletableFuture handleRequest(JsonrpcRequest request) + throws OpenemsNamedException { + return this.handleRequest(request, FunctionUtils::doNothing); + } + + /** + * Handles a {@link JsonrpcRequest}. + * + * @param request the request to handle + * @param callAction the action to execute before the call gets handle by an + * endpoint; can be used to provide additional call properties + * with {@link Call#put(Key, Object)} + * @return the result future + * @throws OpenemsNamedException on error + */ + public CompletableFuture handleRequest(// + JsonrpcRequest request, // + Consumer> callAction // + ) throws OpenemsNamedException { + final var builder = this.builder; + if (builder == null) { + throw new OpenemsException("Not ready"); + } + + final var call = new Call(request); + callAction.accept(call); + builder.handle(call); + + final var response = call.getResponse(); + if (response == null) { + throw new OpenemsException("No response"); + } + + if (response instanceof JsonrpcResponseSuccess success) { + return CompletableFuture + .completedFuture(new GenericJsonrpcResponseSuccess(request.getId(), success.getResult())); + } else if (response instanceof JsonrpcResponseError error) { + return CompletableFuture.failedFuture(error.getOpenemsError().exception(error.getParamsAsObjectArray())); + } + throw new OpenemsException("Unhandled response"); + } + + public void setDebug(boolean debug) { + this.debug = debug; + if (this.builder != null) { + this.builder.setDebug(debug); + } + } + +} diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Subrequest.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Subrequest.java new file mode 100644 index 00000000000..8589d7af913 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Subrequest.java @@ -0,0 +1,40 @@ +package io.openems.edge.common.jsonapi; + +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.JsonElement; + +public final class Subrequest { + + public record Subroute(String[] path, JsonApiBuilder builder) { + + } + + private final JsonElement baseRequest; + private final List subrouteToBuilder = new ArrayList<>(); + + public Subrequest(JsonElement baseRequest) { + this.baseRequest = baseRequest; + } + + /** + * Adds a {@link JsonApiBuilder} for the {@link Subrequest} associated to the + * provided path. + * + * @param builder the {@link JsonApiBuilder} to add at the path + * @param path the path which the builder gets associated to + */ + public void addRpcBuilderFor(JsonApiBuilder builder, String... path) { + this.subrouteToBuilder.add(new Subroute(path, builder)); + } + + public JsonElement getBaseRequest() { + return this.baseRequest; + } + + public List getSubrouteToBuilder() { + return this.subrouteToBuilder; + } + +} diff --git a/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Tag.java b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Tag.java new file mode 100644 index 00000000000..b52e88ef763 --- /dev/null +++ b/io.openems.edge.common/src/io/openems/edge/common/jsonapi/Tag.java @@ -0,0 +1,27 @@ +package io.openems.edge.common.jsonapi; + +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; + +import io.openems.common.jsonrpc.serialization.JsonSerializer; +import io.openems.common.utils.JsonUtils; + +public record Tag(// + String name // +) { + + /** + * Returns a {@link JsonSerializer} for a {@link Tag}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(Tag.class, json -> { + return new Tag(json.getString("name")); + }, obj -> { + return JsonUtils.buildJsonObject() // + .addProperty("name", obj.name()) // + .build(); + }); + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java b/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java index f37d089dce1..28d5b71e4bb 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java +++ b/io.openems.edge.common/src/io/openems/edge/common/sum/Sum.java @@ -7,11 +7,13 @@ import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.channel.DoubleReadChannel; import io.openems.edge.common.channel.IntegerReadChannel; import io.openems.edge.common.channel.LongReadChannel; import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.meta.Meta; import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; import io.openems.edge.common.modbusslave.ModbusType; @@ -273,6 +275,18 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { GRID_MAX_ACTIVE_POWER(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT) // .persistencePriority(PersistencePriority.VERY_HIGH)), + /** + * Grid: Price for Buy-from-Grid. + * + *
    + *
  • Interface: Sum (origin: TimeOfUseTariff) + *
  • Type: Integer + *
  • Unit: Currency (see {@link Meta.ChannelId#CURRENCY}) per MWh + *
+ */ + GRID_BUY_PRICE(Doc.of(OpenemsType.DOUBLE) // + .unit(Unit.MONEY_PER_MEGAWATT_HOUR) // + .persistencePriority(PersistencePriority.VERY_HIGH)), /** * Production: Active Power. * @@ -1192,6 +1206,35 @@ public default void _setGridActivePowerL3(int value) { this.getGridActivePowerL3Channel().setNextValue(value); } + /** + * Gets the Channel for {@link ChannelId#GRID_BUY_PRICE}. + * + * @return the Channel + */ + public default DoubleReadChannel getGridBuyPriceChannel() { + return this.channel(ChannelId.GRID_BUY_PRICE); + } + + /** + * Gets the Buy-from-Grid price [Currency/MWh]. See + * {@link ChannelId#GRID_BUY_PRICE}. + * + * @return the Channel {@link Value} + */ + public default Value getGridBuyPrice() { + return this.getGridBuyPriceChannel().value(); + } + + /** + * Internal method to set the 'nextValue' on {@link ChannelId#GRID_BUY_PRICE} + * Channel. + * + * @param value the next value + */ + public default void _setGridBuyPrice(Double value) { + this.getGridBuyPriceChannel().setNextValue(value); + } + /** * Gets the Channel for {@link ChannelId#GRID_MIN_ACTIVE_POWER}. * diff --git a/io.openems.edge.common/src/io/openems/edge/common/test/AbstractComponentTest.java b/io.openems.edge.common/src/io/openems/edge/common/test/AbstractComponentTest.java index 03bccc75aa1..df40330b7c2 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/test/AbstractComponentTest.java +++ b/io.openems.edge.common/src/io/openems/edge/common/test/AbstractComponentTest.java @@ -528,6 +528,17 @@ public SELF activate(AbstractComponentConfig config) throws Exception { return this.self(); } + /** + * Calls the 'deactivate()' method of the 'system-under-test'. + * + * @return itself, to use as a builder + * @throws Exception on error + */ + public SELF deactivate() throws Exception { + this.callDeactivate(); + return this.self(); + } + private int getConfigChangeCount() throws IOException, InvalidSyntaxException { var result = 0; for (Object object : this.references) { diff --git a/io.openems.edge.common/src/io/openems/edge/common/test/DummyComponentManager.java b/io.openems.edge.common/src/io/openems/edge/common/test/DummyComponentManager.java index 1fee0432111..a3a43bbe07f 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/test/DummyComponentManager.java +++ b/io.openems.edge.common/src/io/openems/edge/common/test/DummyComponentManager.java @@ -7,7 +7,6 @@ import java.util.Collections; import java.util.Hashtable; import java.util.List; -import java.util.concurrent.CompletableFuture; import org.osgi.framework.InvalidSyntaxException; import org.osgi.service.cm.ConfigurationAdmin; @@ -20,25 +19,26 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; +import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; import io.openems.common.jsonrpc.request.GetEdgeConfigRequest; import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest.Property; import io.openems.common.jsonrpc.response.GetEdgeConfigResponse; -import io.openems.common.session.Role; import io.openems.common.types.EdgeConfig; import io.openems.common.utils.JsonUtils; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApiBuilder; import io.openems.edge.common.user.User; /** * Simulates a ComponentManager for the OpenEMS Component test framework. */ -public class DummyComponentManager implements ComponentManager { +public class DummyComponentManager implements ComponentManager, ComponentJsonApi { private final List components = new ArrayList<>(); private final Clock clock; @@ -167,38 +167,29 @@ public Collection> channels() { } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException { - user.assertRoleIsAtLeast("handleJsonrpcRequest", Role.GUEST); + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(GetEdgeConfigRequest.METHOD, call -> { + return new GetEdgeConfigResponse(call.getRequest().getId(), this.getEdgeConfig()); + }); - switch (request.getMethod()) { + builder.handleRequest(CreateComponentConfigRequest.METHOD, call -> { + final var request = CreateComponentConfigRequest.from(call.getRequest()); + this.handleCreateComponentConfigRequest(call.get(EdgeKeys.USER_KEY), request); - case GetEdgeConfigRequest.METHOD: - return this.handleGetEdgeConfigRequest(user, GetEdgeConfigRequest.from(request)); - case CreateComponentConfigRequest.METHOD: - return this.handleCreateComponentConfigRequest(user, CreateComponentConfigRequest.from(request)); - case UpdateComponentConfigRequest.METHOD: - return this.handleUpdateComponentConfigRequest(user, UpdateComponentConfigRequest.from(request)); + return new GenericJsonrpcResponseSuccess(request.getId()); + }); - default: - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } + builder.handleRequest(UpdateComponentConfigRequest.METHOD, call -> { + final var request = UpdateComponentConfigRequest.from(call.getRequest()); + this.handleUpdateComponentConfigRequest(call.get(EdgeKeys.USER_KEY), request); + + return new GenericJsonrpcResponseSuccess(request.getId()); + }); } - /** - * Handles a {@link CreateComponentConfigRequest}. - * - *

- * Only creates the Configuration with the given Properties. Does not actually - * add the component the the dummy component list. - * - * @param user the executing user - * @param request the {@link CreateComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleCreateComponentConfigRequest(User user, - CreateComponentConfigRequest request) throws OpenemsNamedException { + @Override + public void handleCreateComponentConfigRequest(User user, CreateComponentConfigRequest request) + throws OpenemsNamedException { if (this.configurationAdmin == null) { throw new OpenemsException("Can not create Component Config. ConfigurationAdmin is null!"); } @@ -218,27 +209,11 @@ private CompletableFuture handleCreateComponentConfigReq // TODO Auto-generated catch block e.printStackTrace(); } - - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); - } - - /** - * Handles a {@link GetEdgeConfigRequest}. - * - * @param user the {@link User} - * @param request the {@link GetEdgeConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleGetEdgeConfigRequest(User user, - GetEdgeConfigRequest request) throws OpenemsNamedException { - var config = this.getEdgeConfig(); - var response = new GetEdgeConfigResponse(request.getId(), config); - return CompletableFuture.completedFuture(response); } - private CompletableFuture handleUpdateComponentConfigRequest(User user, - UpdateComponentConfigRequest request) throws OpenemsNamedException { + @Override + public void handleUpdateComponentConfigRequest(User user, UpdateComponentConfigRequest request) + throws OpenemsNamedException { if (this.configurationAdmin == null) { throw new OpenemsException("Can not update Component Config. ConfigurationAdmin is null!"); } @@ -257,7 +232,29 @@ private CompletableFuture handleUpdateComponentConfigReq } configuration.update(properties); } - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); + } catch (IOException | InvalidSyntaxException e) { + throw new OpenemsException("Can not update Component Config."); + } + } + + @Override + public void handleDeleteComponentConfigRequest(User user, DeleteComponentConfigRequest request) + throws OpenemsNamedException { + if (this.configurationAdmin == null) { + throw new OpenemsException("Can not delete Component Config. ConfigurationAdmin is null!"); + } + + try { + for (var configuration : this.configurationAdmin.listConfigurations(null)) { + final var props = configuration.getProperties(); + if (props == null) { + continue; + } + if (props.get("id") == null || !props.get("id").equals(request.getComponentId())) { + continue; + } + configuration.delete(); + } } catch (IOException | InvalidSyntaxException e) { throw new OpenemsException("Can not update Component Config."); } diff --git a/io.openems.edge.common/src/io/openems/edge/common/type/Tuple.java b/io.openems.edge.common/src/io/openems/edge/common/type/Tuple.java index 5621c1a6509..04d3292ed7b 100644 --- a/io.openems.edge.common/src/io/openems/edge/common/type/Tuple.java +++ b/io.openems.edge.common/src/io/openems/edge/common/type/Tuple.java @@ -1,4 +1,18 @@ package io.openems.edge.common.type; public record Tuple(A a, B b) { + + /** + * Factory for a {@link Tuple}. + * + * @param Type of a + * @param Type of b + * @param a value a + * @param b value b + * @return a new Tuple + */ + public static Tuple of(A a, B b) { + return new Tuple<>(a, b); + } + } diff --git a/io.openems.edge.common/test/io/openems/edge/common/jsonapi/CallTest.java b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/CallTest.java new file mode 100644 index 00000000000..56a1820b518 --- /dev/null +++ b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/CallTest.java @@ -0,0 +1,81 @@ +package io.openems.edge.common.jsonapi; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.google.gson.JsonObject; + +import io.openems.common.jsonrpc.base.GenericJsonrpcRequest; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; + +public class CallTest { + + @Test + public void testGetSetResponse() { + final var call = new Call( + new GenericJsonrpcRequest("method", new JsonObject())); + + final var exampleResponse = new GenericJsonrpcResponseSuccess(call.getRequest().getId()); + call.setResponse(exampleResponse); + + assertEquals(exampleResponse, call.getResponse()); + } + + @Test + public void testGetPutProperties() { + final var call = new Call( + new GenericJsonrpcRequest("method", new JsonObject())); + + final var dummyKey = new Key<>("dummy", String.class); + + final var dummyValue = "dummyValue"; + call.put(dummyKey, dummyValue); + assertEquals(dummyValue, call.get(dummyKey)); + } + + @Test + public void testMapRequest() { + final var originalRequest = new GenericJsonrpcRequest("method", new JsonObject()); + final var call = new Call(originalRequest); + class DummyRequestClass { + + } + + final var dummyRequest = new DummyRequestClass(); + final var newCall = call.mapRequest(dummyRequest); + + assertEquals(dummyRequest, newCall.getRequest()); + assertEquals(originalRequest, call.getRequest()); + } + + @Test + public void testMapResponse() { + final var call = new Call( + new GenericJsonrpcRequest("method", new JsonObject())); + class DummyResponseClass { + + } + + final var mappedCall = call.mapResponse(); + + final var originalResponse = new GenericJsonrpcResponseSuccess(call.getRequest().getId()); + call.setResponse(originalResponse); + final var mappedResponse = new DummyResponseClass(); + mappedCall.setResponse(mappedResponse); + + assertEquals(originalResponse, call.getResponse()); + assertEquals(mappedResponse, mappedCall.getResponse()); + } + + @Test + public void testGetRequest() { + final var exampleRequest = new GenericJsonrpcRequest("method", new JsonObject()); + final var call = new Call(exampleRequest); + + assertEquals(exampleRequest, call.getRequest()); + } + +} diff --git a/io.openems.edge.common/test/io/openems/edge/common/jsonapi/DummyJsonApi.java b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/DummyJsonApi.java new file mode 100644 index 00000000000..c7ec9307f6f --- /dev/null +++ b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/DummyJsonApi.java @@ -0,0 +1,16 @@ +package io.openems.edge.common.jsonapi; + +public final class DummyJsonApi implements JsonApi { + + private final JsonApiBuilder builder; + + public DummyJsonApi(JsonApiBuilder builder) { + this.builder = builder; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.addBuilder(this.builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/test/io/openems/edge/common/jsonapi/EmptyObject.java b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/EmptyObject.java new file mode 100644 index 00000000000..6c8d0cdc20f --- /dev/null +++ b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/EmptyObject.java @@ -0,0 +1,19 @@ +package io.openems.edge.common.jsonapi; + +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.emptyObjectSerializer; + +import io.openems.common.jsonrpc.serialization.JsonSerializer; + +public record EmptyObject() { + + /** + * Returns a {@link JsonSerializer} for a + * {@link JsonApiBuilderTest.EmptyObject}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return emptyObjectSerializer(EmptyObject::new); + } + +} \ No newline at end of file diff --git a/io.openems.edge.common/test/io/openems/edge/common/jsonapi/EndpointRequestDefinitionBuilderTest.java b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/EndpointRequestDefinitionBuilderTest.java new file mode 100644 index 00000000000..2c6e46460c9 --- /dev/null +++ b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/EndpointRequestDefinitionBuilderTest.java @@ -0,0 +1,48 @@ +package io.openems.edge.common.jsonapi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +public class EndpointRequestDefinitionBuilderTest { + + @Test + public void testSetGetSerializer() { + final var defBuilder = new EndpointRequestDefinitionBuilder(); + defBuilder.setSerializer(EmptyObject.serializer()); + assertNotNull(defBuilder.getSerializer()); + } + + @Test + public void testAddExampleStringRequest() { + final var defBuilder = new EndpointRequestDefinitionBuilder(); + + final var exampleKey = "exampleKey"; + final var exampleObject = new EmptyObject(); + defBuilder.addExample(exampleKey, exampleObject); + assertEquals(1, defBuilder.getExamples().size()); + assertEquals(exampleKey, defBuilder.getExamples().get(0).identifier()); + assertEquals(exampleObject, defBuilder.getExamples().get(0).exampleObject()); + } + + @Test + public void testAddExampleRequest() { + final var defBuilder = new EndpointRequestDefinitionBuilder(); + + final var exampleObject = new EmptyObject(); + defBuilder.addExample(exampleObject); + assertEquals(1, defBuilder.getExamples().size()); + assertEquals(exampleObject, defBuilder.getExamples().get(0).exampleObject()); + } + + @Test + public void testCreateExampleArray() { + final var defBuilder = new EndpointRequestDefinitionBuilder(); + defBuilder.addExample(new EmptyObject()); + defBuilder.setSerializer(EmptyObject.serializer()); + + assertEquals(1, defBuilder.createExampleArray().size()); + } + +} diff --git a/io.openems.edge.common/test/io/openems/edge/common/jsonapi/EndpointResponseDefinitionBuilderTest.java b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/EndpointResponseDefinitionBuilderTest.java new file mode 100644 index 00000000000..487d69b6937 --- /dev/null +++ b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/EndpointResponseDefinitionBuilderTest.java @@ -0,0 +1,48 @@ +package io.openems.edge.common.jsonapi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +public class EndpointResponseDefinitionBuilderTest { + + @Test + public void testSetGetSerializer() { + final var defBuilder = new EndpointResponseDefinitionBuilder(); + defBuilder.setSerializer(EmptyObject.serializer()); + assertNotNull(defBuilder.getSerializer()); + } + + @Test + public void testAddExampleStringResponse() { + final var defBuilder = new EndpointResponseDefinitionBuilder(); + + final var exampleKey = "exampleKey"; + final var exampleObject = new EmptyObject(); + defBuilder.addExample(exampleKey, exampleObject); + assertEquals(1, defBuilder.getExamples().size()); + assertEquals(exampleKey, defBuilder.getExamples().get(0).identifier()); + assertEquals(exampleObject, defBuilder.getExamples().get(0).exampleObject()); + } + + @Test + public void testAddExampleResponse() { + final var defBuilder = new EndpointResponseDefinitionBuilder(); + + final var exampleObject = new EmptyObject(); + defBuilder.addExample(exampleObject); + assertEquals(1, defBuilder.getExamples().size()); + assertEquals(exampleObject, defBuilder.getExamples().get(0).exampleObject()); + } + + @Test + public void testCreateExampleArray() { + final var defBuilder = new EndpointResponseDefinitionBuilder(); + defBuilder.addExample(new EmptyObject()); + defBuilder.setSerializer(EmptyObject.serializer()); + + assertEquals(1, defBuilder.createExampleArray().size()); + } + +} diff --git a/io.openems.edge.common/test/io/openems/edge/common/jsonapi/JsonApiBuilderTest.java b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/JsonApiBuilderTest.java new file mode 100644 index 00000000000..290d565adbf --- /dev/null +++ b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/JsonApiBuilderTest.java @@ -0,0 +1,188 @@ +package io.openems.edge.common.jsonapi; + +import static java.util.Collections.emptyList; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; + +import org.junit.Test; + +import com.google.gson.JsonObject; + +import io.openems.common.jsonrpc.base.GenericJsonrpcRequest; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; +import io.openems.common.utils.FunctionUtils; + +public class JsonApiBuilderTest { + + @Test + public void testAddMethodHandler() { + final var builder = new JsonApiBuilder(); + + final var testMethod = "method"; + builder.handleRequest(testMethod, t -> null); + + assertEquals(1, builder.getEndpoints().size()); + final var addedEndpoint = builder.getEndpoints().get(testMethod); + assertNotNull(addedEndpoint); + } + + @Test + public void testAddMethodHandler2() { + final var builder = new JsonApiBuilder(); + + final var testMethod = "method"; + final var dummyDescription = "description"; + builder.rpc(testMethod, endpoint -> { + endpoint.setDescription(dummyDescription); + }, FunctionUtils::doNothing); + + assertEquals(1, builder.getEndpoints().size()); + final var addedEndpoint = builder.getEndpoints().get(testMethod); + assertNotNull(addedEndpoint); + assertEquals(dummyDescription, addedEndpoint.getDef().getDescription()); + } + + @Test + public void testAddMethodHandler3() { + final var builder = new JsonApiBuilder(); + + final var testMethod = "method"; + final var dummyDescription = "description"; + builder.rpc(testMethod, endpoint -> { + endpoint.setDescription(dummyDescription); + }, () -> List.of(new Subrequest(new JsonObject())), FunctionUtils::doNothing); + + assertEquals(1, builder.getEndpoints().size()); + final var addedEndpoint = builder.getEndpoints().get(testMethod); + assertNotNull(addedEndpoint); + assertEquals(dummyDescription, addedEndpoint.getDef().getDescription()); + final var subroutes = addedEndpoint.getSubroutes().get(); + assertEquals(1, subroutes.size()); + } + + @Test + public void testAddMethodHandler4() { + final var builder = new JsonApiBuilder(); + + final var testMethod = "method"; + final var dummyDescription = "description"; + builder.rpc(testMethod, endpoint -> { + endpoint.setDescription(dummyDescription); + }, () -> List.of(new Subrequest(new JsonObject())), FunctionUtils::doNothing, EmptyObject.serializer()); + + assertEquals(1, builder.getEndpoints().size()); + final var addedEndpoint = builder.getEndpoints().get(testMethod); + assertNotNull(addedEndpoint); + assertEquals(dummyDescription, addedEndpoint.getDef().getDescription()); + assertNotNull(addedEndpoint.getDef().getEndpointRequestBuilder().getSerializer()); + final var subroutes = addedEndpoint.getSubroutes().get(); + assertEquals(1, subroutes.size()); + } + + @Test + public void testAddMethodHandler5() { + final var builder = new JsonApiBuilder(); + + final var testMethod = "method"; + final var dummyDescription = "description"; + builder.rpc(testMethod, endpoint -> { + endpoint.setDescription(dummyDescription); + }, FunctionUtils::doNothing, EmptyObject.serializer(), EmptyObject.serializer()); + + assertEquals(1, builder.getEndpoints().size()); + final var addedEndpoint = builder.getEndpoints().get(testMethod); + assertNotNull(addedEndpoint); + assertEquals(dummyDescription, addedEndpoint.getDef().getDescription()); + assertNotNull(addedEndpoint.getDef().getEndpointRequestBuilder().getSerializer()); + assertNotNull(addedEndpoint.getDef().getEndpointResponseBuilder().getSerializer()); + } + + @Test + public void testDelegate() throws Exception { + final var builder = new JsonApiBuilder(); + + final var testMethod = "method"; + final var dummyDescription = "description"; + builder.delegate(testMethod, endpoint -> { + endpoint.setDescription(dummyDescription); + }, call -> { + return new GenericJsonrpcRequest("method2", new JsonObject()); + }, Function.identity(), Function.identity(), () -> emptyList()); + + final var isMethod2Called = new AtomicBoolean(false); + builder.handleRequest("method2", call -> { + isMethod2Called.set(true); + return null; + }); + + assertEquals(2, builder.getEndpoints().size()); + final var addedEndpoint = builder.getEndpoints().get(testMethod); + assertNotNull(addedEndpoint); + assertEquals(dummyDescription, addedEndpoint.getDef().getDescription()); + + final var testCall = new Call( + new GenericJsonrpcRequest(testMethod, new JsonObject())); + builder.handle(testCall); + assertTrue(isMethod2Called.get()); + } + + @Test + public void testCallEndpoint() { + final var builder = new JsonApiBuilder(); + + final var testMethod = "method"; + builder.handleRequest(testMethod, call -> { + return new GenericJsonrpcResponseSuccess(call.getRequest().getId()); + }); + + assertEquals(1, builder.getEndpoints().size()); + final var addedEndpoint = builder.getEndpoints().get(testMethod); + assertNotNull(addedEndpoint); + + final var testCall = new Call( + new GenericJsonrpcRequest(testMethod, new JsonObject())); + builder.handle(testCall); + + assertNotNull(testCall.getResponse()); + } + + @Test + public void testRemoveEndpoint() throws Exception { + final var builder = new JsonApiBuilder(); + + final var testMethod = "method"; + builder.handleRequest(testMethod, t -> null); + + assertEquals(1, builder.getEndpoints().size()); + + builder.removeEndpoint(testMethod); + assertEquals(0, builder.getEndpoints().size()); + } + + @Test + public void testAddBuilder() throws Exception { + final var builder = new JsonApiBuilder(); + final var subBuilder = new JsonApiBuilder(); + + builder.addBuilder(subBuilder); + + final var testMethod = "subMethod"; + subBuilder.handleRequest(testMethod, call -> { + return new GenericJsonrpcResponseSuccess(call.getRequest().getId()); + }); + + final var testCall = new Call( + new GenericJsonrpcRequest(testMethod, new JsonObject())); + builder.handle(testCall); + + assertNotNull(testCall.getResponse()); + } + +} diff --git a/io.openems.edge.common/test/io/openems/edge/common/jsonapi/JsonrpcRoleEndpointGuardTest.java b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/JsonrpcRoleEndpointGuardTest.java new file mode 100644 index 00000000000..0e7cfc5eed6 --- /dev/null +++ b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/JsonrpcRoleEndpointGuardTest.java @@ -0,0 +1,48 @@ +package io.openems.edge.common.jsonapi; + +import static io.openems.edge.common.test.DummyUser.DUMMY_ADMIN; +import static io.openems.edge.common.test.DummyUser.DUMMY_INSTALLER; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.google.gson.JsonObject; + +import io.openems.common.jsonrpc.base.GenericJsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; +import io.openems.common.session.Role; + +public class JsonrpcRoleEndpointGuardTest { + + @Test + public void testTest() throws Exception { + final var guard = new JsonrpcRoleEndpointGuard(Role.ADMIN); + + final var call = new Call( + new GenericJsonrpcRequest("method", new JsonObject())); + + call.put(EdgeKeys.USER_KEY, DUMMY_ADMIN); + guard.test(call); + } + + @Test(expected = Exception.class) + public void testTestException() throws Exception { + final var guard = new JsonrpcRoleEndpointGuard(Role.ADMIN); + + final var call = new Call( + new GenericJsonrpcRequest("method", new JsonObject())); + + call.put(EdgeKeys.USER_KEY, DUMMY_INSTALLER); + guard.test(call); + } + + @Test + public void testGetRole() { + final var role = Role.ADMIN; + final var guard = new JsonrpcRoleEndpointGuard(role); + + assertEquals(role, guard.getRole()); + } + +} diff --git a/io.openems.edge.common/test/io/openems/edge/common/jsonapi/MultipleJsonApiBinderTest.java b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/MultipleJsonApiBinderTest.java new file mode 100644 index 00000000000..3788513ff83 --- /dev/null +++ b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/MultipleJsonApiBinderTest.java @@ -0,0 +1,54 @@ +package io.openems.edge.common.jsonapi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import io.openems.common.utils.FunctionUtils; + +public class MultipleJsonApiBinderTest { + + @Test + public void testBindJsonApi() { + final var binder = new MultipleJsonApiBinder(); + + binder.bindJsonApi(new DummyJsonApi(new JsonApiBuilder().rpc("method1", FunctionUtils::doNothing))); + binder.bindJsonApi(new DummyJsonApi(new JsonApiBuilder().rpc("method2", FunctionUtils::doNothing))); + + assertTrue(binder.getJsonApiBuilder().getEndpoints().containsKey("method1")); + assertTrue(binder.getJsonApiBuilder().getEndpoints().containsKey("method2")); + assertEquals(2, binder.getJsonApiBuilder().getEndpoints().size()); + + binder.bindJsonApi(new DummyJsonApi(new JsonApiBuilder().rpc("method3", FunctionUtils::doNothing))); + + assertTrue(binder.getJsonApiBuilder().getEndpoints().containsKey("method3")); + assertEquals(3, binder.getJsonApiBuilder().getEndpoints().size()); + } + + @Test + public void testUnbindJsonApi() { + final var binder = new MultipleJsonApiBinder(); + + final var dummyApi1 = new DummyJsonApi(new JsonApiBuilder().rpc("method1", FunctionUtils::doNothing)); + binder.bindJsonApi(dummyApi1); + final var dummyApi2 = new DummyJsonApi(new JsonApiBuilder().rpc("method2", FunctionUtils::doNothing)); + binder.bindJsonApi(dummyApi2); + + assertTrue(binder.getJsonApiBuilder().getEndpoints().containsKey("method1")); + assertTrue(binder.getJsonApiBuilder().getEndpoints().containsKey("method2")); + assertEquals(2, binder.getJsonApiBuilder().getEndpoints().size()); + + final var dummyApi3 = new DummyJsonApi(new JsonApiBuilder().rpc("method3", FunctionUtils::doNothing)); + binder.bindJsonApi(dummyApi3); + + assertTrue(binder.getJsonApiBuilder().getEndpoints().containsKey("method3")); + assertEquals(3, binder.getJsonApiBuilder().getEndpoints().size()); + + binder.unbindJsonApi(dummyApi2); + assertTrue(binder.getJsonApiBuilder().getEndpoints().containsKey("method1")); + assertTrue(binder.getJsonApiBuilder().getEndpoints().containsKey("method3")); + assertEquals(2, binder.getJsonApiBuilder().getEndpoints().size()); + } + +} diff --git a/io.openems.edge.common/test/io/openems/edge/common/jsonapi/SingleJsonApiBinderTest.java b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/SingleJsonApiBinderTest.java new file mode 100644 index 00000000000..18a0b620eea --- /dev/null +++ b/io.openems.edge.common/test/io/openems/edge/common/jsonapi/SingleJsonApiBinderTest.java @@ -0,0 +1,55 @@ +package io.openems.edge.common.jsonapi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.Optional; +import java.util.UUID; + +import org.junit.Test; + +import com.google.gson.JsonObject; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.base.GenericJsonrpcRequest; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; + +public class SingleJsonApiBinderTest { + + @Test + public void testBind() throws Exception { + final var binder = new SingleJsonApiBinder(); + + final var id = UUID.randomUUID(); + final var response = new GenericJsonrpcResponseSuccess(id); + binder.bind(new DummyJsonApi(new JsonApiBuilder().handleRequest("method", call -> response))); + + final var handledResponse = binder + .handleRequest(new GenericJsonrpcRequest(id, "method", new JsonObject(), Optional.empty())).get(); + assertEquals(response.getResult(), handledResponse.getResult()); + } + + @Test + public void testUnbind() throws Exception { + final var binder = new SingleJsonApiBinder(); + + final var id = UUID.randomUUID(); + final var response = new GenericJsonrpcResponseSuccess(id); + binder.bind(new DummyJsonApi(new JsonApiBuilder().handleRequest("method", call -> response))); + + final var handledResponse = binder + .handleRequest(new GenericJsonrpcRequest(id, "method", new JsonObject(), Optional.empty())).get(); + assertEquals(response.getResult(), handledResponse.getResult()); + + binder.unbind(); + + OpenemsNamedException exception = null; + try { + binder.handleRequest(new GenericJsonrpcRequest("method", new JsonObject())).get(); + } catch (OpenemsNamedException e) { + exception = e; + } + assertNotNull(exception); + } + +} diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendOnRequest.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendOnRequest.java new file mode 100644 index 00000000000..c3d38665a53 --- /dev/null +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/BackendOnRequest.java @@ -0,0 +1,84 @@ +package io.openems.edge.controller.api.backend; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +import org.java_websocket.WebSocket; +import org.osgi.service.component.ComponentServiceObjects; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ServiceScope; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.utils.FunctionUtils; +import io.openems.common.websocket.OnRequest; +import io.openems.edge.common.jsonapi.Call; +import io.openems.edge.common.jsonapi.SingleJsonApiBinder; +import io.openems.edge.controller.api.backend.handler.RootRequestHandler; + +@Component(// + scope = ServiceScope.PROTOTYPE, // + service = { BackendOnRequest.class, OnRequest.class } // +) +public class BackendOnRequest implements OnRequest { + + @Component(service = BackendOnRequest.Factory.class) + public static class Factory { + + @Reference + private ComponentServiceObjects cso; + + /** + * Returns a new {@link BackendOnRequest} service object. + * + * @return the created {@link BackendOnRequest} object + * @see #unget(BackendOnRequest) + */ + public BackendOnRequest get() { + return this.cso.getService(); + } + + /** + * Releases the {@link BackendOnRequest} service object. + * + * @param service a {@link BackendOnRequest} provided by this factory + * @see #get() + */ + public void unget(BackendOnRequest service) { + if (service == null) { + return; + } + this.cso.ungetService(service); + } + + } + + private final SingleJsonApiBinder apiBinder = new SingleJsonApiBinder(); + private Consumer> onCall = FunctionUtils::doNothing; + + @Activate + public BackendOnRequest(@Reference RootRequestHandler handler) { + this.apiBinder.bind(handler); + } + + @Override + public CompletableFuture run(// + final WebSocket ws, // + final JsonrpcRequest request // + ) throws OpenemsNamedException { + return this.apiBinder.handleRequest(request, this.onCall); + } + + public void setOnCall(Consumer> callAction) { + this.onCall = callAction == null ? FunctionUtils::doNothing : callAction; + } + + public void setDebug(boolean debug) { + this.apiBinder.setDebug(debug); + } + +} diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/ControllerApiBackendImpl.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/ControllerApiBackendImpl.java index eefbc0b7be3..50f6109cba7 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/ControllerApiBackendImpl.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/ControllerApiBackendImpl.java @@ -15,8 +15,6 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import org.ops4j.pax.logging.spi.PaxAppender; -import org.ops4j.pax.logging.spi.PaxLoggingEvent; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -36,7 +34,6 @@ import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.jsonrpc.notification.EdgeConfigNotification; -import io.openems.common.jsonrpc.notification.SystemLogNotification; import io.openems.common.oem.OpenemsEdgeOem; import io.openems.common.types.EdgeConfig; import io.openems.common.utils.ThreadPoolUtils; @@ -46,29 +43,31 @@ import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.cycle.Cycle; import io.openems.edge.common.event.EdgeEventConstants; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.Key; import io.openems.edge.common.user.User; import io.openems.edge.controller.api.Controller; +import io.openems.edge.controller.api.backend.api.ControllerApiBackend; import io.openems.edge.controller.api.common.ApiWorker; +import io.openems.edge.controller.api.common.handler.ComponentConfigRequestHandler; @Designate(ocd = Config.class, factory = true) @Component(// name = "Controller.Api.Backend", // immediate = true, // - configurationPolicy = ConfigurationPolicy.REQUIRE, // - property = { // - "org.ops4j.pax.logging.appender.name=Controller.Api.Backend", // - } // + configurationPolicy = ConfigurationPolicy.REQUIRE // ) @EventTopics({ // EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE, // EdgeEventConstants.TOPIC_CONFIG_UPDATE // }) public class ControllerApiBackendImpl extends AbstractOpenemsComponent - implements ControllerApiBackend, Controller, JsonApi, OpenemsComponent, PaxAppender, EventHandler { + implements ControllerApiBackend, Controller, OpenemsComponent, EventHandler { protected static final String COMPONENT_NAME = "Controller.Api.Backend"; + public static final Key WEBSOCKET_CLIENT_KEY = new Key<>("websocketClient", WebsocketClient.class); + protected final SendChannelValuesWorker sendChannelValuesWorker = new SendChannelValuesWorker(this); protected final ApiWorker apiWorker = new ApiWorker(this); @@ -76,21 +75,22 @@ public class ControllerApiBackendImpl extends AbstractOpenemsComponent @Reference private OpenemsEdgeOem oem; + @Reference + protected ComponentManager componentManager; + @Reference + protected Cycle cycle; @Reference private ResendHistoricDataWorkerFactory resendHistoricDataWorkerFactory; protected ResendHistoricDataWorker resendHistoricDataWorker; @Reference - protected ComponentManager componentManager; - - @Reference - protected Cycle cycle; + private BackendOnRequest.Factory requestHandlerFactory; + protected BackendOnRequest requestHandler; protected WebsocketClient websocket = null; protected Config config; /** Used for SubscribeSystemLogRequests. */ - private boolean isSystemLogSubscribed = false; private ScheduledExecutorService executor; public ControllerApiBackendImpl() { @@ -99,20 +99,17 @@ public ControllerApiBackendImpl() { Controller.ChannelId.values(), // ControllerApiBackend.ChannelId.values() // ); - this.apiWorker.setLogChannel(this.getApiWorkerLogChannel()); } - /** - * Activation method. - * - * @param context the {@link ComponentContext} - * @param config the {@link Config} - */ @Activate private void activate(ComponentContext context, Config config) { this.config = config; super.activate(context, config.id(), config.alias(), config.enabled()); + this.apiWorker.setLogChannel(this.getApiWorkerLogChannel()); + this.resendHistoricDataWorker = this.resendHistoricDataWorkerFactory.get(); + this.requestHandler = this.requestHandlerFactory.get(); + if (!this.isEnabled()) { return; } @@ -159,11 +156,18 @@ private void activate(ComponentContext context, Config config) { t -> this.websocket.sendMessage(t) // )); this.resendHistoricDataWorker.activate(this.id(), false); + + this.requestHandler.setOnCall(call -> { + call.put(WEBSOCKET_CLIENT_KEY, this.websocket); + call.put(ComponentConfigRequestHandler.API_WORKER_KEY, this.apiWorker); + call.put(EdgeKeys.IS_FROM_BACKEND_KEY, true); + }); + this.requestHandler.setDebug(config.debugMode()); } @Override @Deactivate - protected void deactivate() { + protected synchronized void deactivate() { super.deactivate(); this.resendHistoricDataWorkerFactory.unget(this.resendHistoricDataWorker); this.resendHistoricDataWorker = null; @@ -194,55 +198,33 @@ protected void logError(Logger log, String message) { super.logError(log, message); } - /** - * Activates/deactivates subscription to System-Log. - * - *

- * If activated, all System-Log events are sent via - * {@link SystemLogNotification}s. - * - * @param isSystemLogSubscribed true to activate - */ - protected void setSystemLogSubscribed(boolean isSystemLogSubscribed) { - this.isSystemLogSubscribed = isSystemLogSubscribed; - } - - @Override - public void doAppend(PaxLoggingEvent event) { - if (!this.isSystemLogSubscribed) { - return; - } - var ws = this.websocket; - if (ws == null) { - return; - } - var notification = SystemLogNotification.fromPaxLoggingEvent(event); - ws.sendMessage(notification); - } - @Override public void handleEvent(Event event) { - if (!this.isEnabled()) { - return; - } - switch (event.getTopic()) { - case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: - this.sendChannelValuesWorker.collectData(); - break; - - case EdgeEventConstants.TOPIC_CONFIG_UPDATE: - // Send new EdgeConfig - var config = (EdgeConfig) event.getProperty(EdgeEventConstants.TOPIC_CONFIG_UPDATE_KEY); - var message = new EdgeConfigNotification(config); - var ws = this.websocket; - if (ws == null) { + try { + if (!this.isEnabled()) { return; } - ws.sendMessage(message); - - // Trigger sending of all channel values, because a Component might have - // disappeared - this.sendChannelValuesWorker.sendValuesOfAllChannelsOnce(); + switch (event.getTopic()) { + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: + this.sendChannelValuesWorker.collectData(); + break; + + case EdgeEventConstants.TOPIC_CONFIG_UPDATE: + // Send new EdgeConfig + var config = (EdgeConfig) event.getProperty(EdgeEventConstants.TOPIC_CONFIG_UPDATE_KEY); + var message = new EdgeConfigNotification(config); + var ws = this.websocket; + if (ws == null) { + return; + } + ws.sendMessage(message); + + // Trigger sending of all channel values, because a Component might have + // disappeared + this.sendChannelValuesWorker.sendValuesOfAllChannelsOnce(); + } + } catch (Exception e) { + e.printStackTrace(); } } @@ -281,9 +263,8 @@ public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialD } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) + public CompletableFuture sendRequest(User user, JsonrpcRequest request) throws OpenemsNamedException { - // delegates request to actual backend return this.websocket.sendRequest(request); } diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnRequest.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnRequest.java deleted file mode 100644 index d49fcfbbd19..00000000000 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/OnRequest.java +++ /dev/null @@ -1,292 +0,0 @@ -package io.openems.edge.controller.api.backend; - -import java.util.concurrent.CompletableFuture; - -import org.java_websocket.WebSocket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.common.exceptions.NotImplementedException; -import io.openems.common.exceptions.OpenemsError; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; -import io.openems.common.jsonrpc.request.AuthenticatedRpcRequest; -import io.openems.common.jsonrpc.request.ComponentJsonApiRequest; -import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; -import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; -import io.openems.common.jsonrpc.request.GetEdgeConfigRequest; -import io.openems.common.jsonrpc.request.SetChannelValueRequest; -import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest; -import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; -import io.openems.common.jsonrpc.request.UpdateUserLanguageRequest; -import io.openems.common.jsonrpc.request.UpdateUserSettingsRequest; -import io.openems.common.jsonrpc.response.AuthenticatedRpcResponse; -import io.openems.common.session.Role; -import io.openems.edge.common.component.ComponentManager; -import io.openems.edge.common.jsonapi.JsonApi; -import io.openems.edge.common.user.User; - -public class OnRequest implements io.openems.common.websocket.OnRequest { - - private final Logger log = LoggerFactory.getLogger(OnRequest.class); - private final ControllerApiBackendImpl parent; - - public OnRequest(ControllerApiBackendImpl parent) { - this.parent = parent; - } - - @Override - public CompletableFuture run(WebSocket ws, JsonrpcRequest request) - throws OpenemsException, OpenemsNamedException { - switch (request.getMethod()) { - - case AuthenticatedRpcRequest.METHOD: - return this.handleAuthenticatedRpcRequest(AuthenticatedRpcRequest.from(request, User::from)); - - default: - this.parent.logWarn(this.log, "Unhandled Request: " + request); - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } - } - - /** - * Handles a {@link AuthenticatedRpcRequest}. - * - * @param authenticatedRpcRequest the {@link AuthenticatedRpcRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleAuthenticatedRpcRequest( - AuthenticatedRpcRequest authenticatedRpcRequest) throws OpenemsNamedException { - var user = authenticatedRpcRequest.getUser(); - var request = authenticatedRpcRequest.getPayload(); - - final var resultFuture = switch (request.getMethod()) { - - case GetEdgeConfigRequest.METHOD -> // - this.handleGetEdgeConfigRequest(user, GetEdgeConfigRequest.from(request)); - - case CreateComponentConfigRequest.METHOD -> // - this.handleCreateComponentConfigRequest(user, CreateComponentConfigRequest.from(request)); - - case UpdateComponentConfigRequest.METHOD -> // - this.handleUpdateComponentConfigRequest(user, UpdateComponentConfigRequest.from(request)); - - case DeleteComponentConfigRequest.METHOD -> // - this.handleDeleteComponentConfigRequest(user, DeleteComponentConfigRequest.from(request)); - - case SetChannelValueRequest.METHOD -> // - this.handleSetChannelValueRequest(user, SetChannelValueRequest.from(request)); - - case ComponentJsonApiRequest.METHOD -> // - this.handleComponentJsonApiRequest(user, ComponentJsonApiRequest.from(request)); - - case SubscribeSystemLogRequest.METHOD -> // - this.handleSubscribeSystemLogRequest(user, SubscribeSystemLogRequest.from(request)); - - case UpdateUserLanguageRequest.METHOD -> // - this.handleUpdateUserLanguageRequest(user, UpdateUserLanguageRequest.from(request)); - - case UpdateUserSettingsRequest.METHOD -> // - this.handleUpdateUserSettingsRequest(user, UpdateUserSettingsRequest.from(request)); - - default -> { - this.parent.logWarn(this.log, "Unhandled Request: " + request); - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } - }; - - var result = new CompletableFuture(); - resultFuture.whenComplete((r, ex) -> { - if (ex != null) { - result.completeExceptionally(ex); - } else if (r != null) { - result.complete(new AuthenticatedRpcResponse(authenticatedRpcRequest.getId(), r)); - } else { - result.completeExceptionally( - new OpenemsNamedException(OpenemsError.JSONRPC_UNHANDLED_METHOD, request.getMethod())); - } - }); - return result; - } - - /** - * Handles a {@link GetEdgeConfigRequest}. - * - * @param user the {@link User} - * @param getEdgeConfigRequest the {@link GetEdgeConfigRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleGetEdgeConfigRequest(User user, - GetEdgeConfigRequest getEdgeConfigRequest) throws OpenemsNamedException { - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, getEdgeConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a {@link CreateComponentConfigRequest}. - * - * @param user the {@link User} - * @param createComponentConfigRequest the {@link CreateComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleCreateComponentConfigRequest(User user, - CreateComponentConfigRequest createComponentConfigRequest) throws OpenemsNamedException { - user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.INSTALLER); - - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, - createComponentConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a {@link UpdateComponentConfigRequest}. - * - * @param user the {@link User} - * @param updateComponentConfigRequest the {@link UpdateComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleUpdateComponentConfigRequest(User user, - UpdateComponentConfigRequest updateComponentConfigRequest) throws OpenemsNamedException { - user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.OWNER); - - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, - updateComponentConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a {@link DeleteComponentConfigRequest}. - * - * @param user the {@link User} - * @param deleteComponentConfigRequest the {@link DeleteComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleDeleteComponentConfigRequest(User user, - DeleteComponentConfigRequest deleteComponentConfigRequest) throws OpenemsNamedException { - user.assertRoleIsAtLeast(DeleteComponentConfigRequest.METHOD, Role.INSTALLER); - - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, - deleteComponentConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a {@link SetChannelValueRequest}. - * - * @param user the {@link User} - * @param request the {@link SetChannelValueRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleSetChannelValueRequest(User user, - SetChannelValueRequest request) throws OpenemsNamedException { - user.assertRoleIsAtLeast(SetChannelValueRequest.METHOD, Role.ADMIN); - - return this.parent.apiWorker.handleSetChannelValueRequest(this.parent.componentManager, user, request); - } - - /** - * Handles a {@link ComponentJsonApiRequest}. - * - * @param user the {@link User} - * @param request the {@link ComponentJsonApiRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleComponentJsonApiRequest(User user, - ComponentJsonApiRequest request) throws OpenemsNamedException { - // get Component - var componentId = request.getComponentId(); - var component = this.parent.componentManager.getComponent(componentId); - - if (component == null) { - throw new OpenemsException("Unable to find Component [" + componentId + "]"); - } - - if (!(component instanceof JsonApi)) { - throw new OpenemsException("Component [" + componentId + "] is no JsonApi"); - } - - // call JsonApi - var jsonApi = (JsonApi) component; - CompletableFuture responseFuture = jsonApi.handleJsonrpcRequest(user, - request.getPayload()); - - // handle null response - if (responseFuture == null) { - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getPayload().getMethod()); - } - - // Wrap reply in EdgeRpcResponse - var edgeRpcResponse = new CompletableFuture(); - responseFuture.whenComplete((r, ex) -> { - if (ex != null) { - edgeRpcResponse.completeExceptionally(ex); - } else if (r != null) { - edgeRpcResponse.complete(new GenericJsonrpcResponseSuccess(request.getId(), r.getResult())); - } else { - edgeRpcResponse.completeExceptionally(new OpenemsNamedException(OpenemsError.JSONRPC_UNHANDLED_METHOD, - request.getPayload().getMethod())); - } - }); - - return edgeRpcResponse; - } - - /** - * Handles a {@link SubscribeSystemLogRequest}. - * - * @param user the {@link User} - * @param request the {@link SubscribeSystemLogRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleSubscribeSystemLogRequest(User user, - SubscribeSystemLogRequest request) throws OpenemsNamedException { - this.parent.setSystemLogSubscribed(request.isSubscribe()); - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); - } - - /** - * Handles a {@link UpdateUserLanguageRequest}. - * - * @param user the {@link User} - * @param request the {@link UpdateUserLanguageRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleUpdateUserLanguageRequest(User user, - UpdateUserLanguageRequest request) throws OpenemsNamedException { - throw new NotImplementedException("Edge backend api update user language not implemented"); - } - - /** - * Handles a {@link UpdateUserSettingsRequest}. - * - * @param user the {@link User} - * @param request the {@link UpdateUserSettingsRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleUpdateUserSettingsRequest(User user, - UpdateUserSettingsRequest request) throws OpenemsNamedException { - throw new NotImplementedException("Edge backend api update user settings not implemented"); - } - -} diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/WebsocketClient.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/WebsocketClient.java index 45a9800a44b..9730473734e 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/WebsocketClient.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/WebsocketClient.java @@ -19,7 +19,6 @@ public class WebsocketClient extends AbstractWebsocketClient { private final ControllerApiBackendImpl parent; private final OnOpen onOpen; - private final OnRequest onRequest; private final OnNotification onNotification; private final OnError onError; private final OnClose onClose; @@ -29,7 +28,6 @@ protected WebsocketClient(ControllerApiBackendImpl parent, String name, URI serv super(name, serverUri, httpHeaders, proxy); this.parent = parent; this.onOpen = new OnOpen(parent); - this.onRequest = new OnRequest(parent); this.onNotification = new OnNotification(parent); this.onError = new OnError(parent); this.onClose = (ws, code, reason, remote) -> { @@ -45,8 +43,8 @@ public OnOpen getOnOpen() { } @Override - public OnRequest getOnRequest() { - return this.onRequest; + public BackendOnRequest getOnRequest() { + return this.parent.requestHandler; } @Override diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/ControllerApiBackend.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/api/ControllerApiBackend.java similarity index 73% rename from io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/ControllerApiBackend.java rename to io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/api/ControllerApiBackend.java index 555420f97c8..90023b35f81 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/ControllerApiBackend.java +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/api/ControllerApiBackend.java @@ -1,21 +1,25 @@ -package io.openems.edge.controller.api.backend; +package io.openems.edge.controller.api.backend.api; + +import java.util.concurrent.CompletableFuture; -import org.ops4j.pax.logging.spi.PaxAppender; import org.osgi.service.event.EventHandler; import io.openems.common.channel.Level; import io.openems.common.channel.PersistencePriority; import io.openems.common.channel.Unit; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.channel.LongReadChannel; import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.StringReadChannel; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.user.User; import io.openems.edge.controller.api.Controller; -public interface ControllerApiBackend extends Controller, JsonApi, OpenemsComponent, PaxAppender, EventHandler { +public interface ControllerApiBackend extends Controller, OpenemsComponent, EventHandler { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { API_WORKER_LOG(Doc.of(OpenemsType.STRING) // @@ -76,4 +80,15 @@ public default LongReadChannel getLastSuccessFulResendChannel() { */ public boolean isConnected(); + /** + * Sends the request to the connected backend. + * + * @param user the user + * @param request the request to send + * @return the result future + * @throws OpenemsNamedException on error + */ + public CompletableFuture sendRequest(User user, JsonrpcRequest request) + throws OpenemsNamedException; + } diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/api/package-info.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/api/package-info.java new file mode 100644 index 00000000000..ad5a110d7cf --- /dev/null +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/api/package-info.java @@ -0,0 +1,3 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +@org.osgi.annotation.bundle.Export +package io.openems.edge.controller.api.backend.api; diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/AuthenticatedRequestHandler.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/AuthenticatedRequestHandler.java new file mode 100644 index 00000000000..e95c94ad568 --- /dev/null +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/AuthenticatedRequestHandler.java @@ -0,0 +1,65 @@ +package io.openems.edge.controller.api.backend.handler; + +import java.util.List; + +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; + +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.request.AuthenticatedRpcRequest; +import io.openems.common.jsonrpc.response.AuthenticatedRpcResponse; +import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.jsonapi.MultipleJsonApiBinder; +import io.openems.edge.common.jsonapi.Subrequest; +import io.openems.edge.common.user.User; + +@Component(property = { "entry=" + RootRequestHandler.ENTRY_POINT }) +public class AuthenticatedRequestHandler implements JsonApi { + + public static final String ENTRY_POINT = "edge.backend.authenticated"; + + private final MultipleJsonApiBinder binder = new MultipleJsonApiBinder(); + + @Reference(// + target = "(entry=" + ENTRY_POINT + ")", // + bind = "bindHandler", unbind = "unbindHandler", // + policyOption = ReferencePolicyOption.GREEDY, // + policy = ReferencePolicy.DYNAMIC, // + cardinality = ReferenceCardinality.MULTIPLE // + ) + protected void bindHandler(JsonApi handler) { + this.binder.bindJsonApi(handler); + } + + protected void unbindHandler(JsonApi handler) { + this.binder.unbindJsonApi(handler); + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder b) { + b.delegate(AuthenticatedRpcRequest.METHOD, endpoint -> { + + }, t -> { + final var authenticatedRpcRequest = AuthenticatedRpcRequest.from(t.getRequest(), User::from); + t.put(EdgeKeys.USER_KEY, authenticatedRpcRequest.getUser()); + return authenticatedRpcRequest.getPayload(); + }, c -> this.binder.getJsonApiBuilder(), response -> { + // wrap response in a AuthenticatedRpcResponse if successful + if (response instanceof JsonrpcResponseSuccess success) { + return new AuthenticatedRpcResponse(response.getId(), success); + } + return response; + }, () -> { + final var subrequest = new Subrequest(JsonUtils.buildJsonObject().build()); + subrequest.addRpcBuilderFor(this.binder.getJsonApiBuilder(), "payload"); + return List.of(subrequest); + }); + } + +} diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/BindingComponentConfigRequestHandler.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/BindingComponentConfigRequestHandler.java new file mode 100644 index 00000000000..e726a07b404 --- /dev/null +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/BindingComponentConfigRequestHandler.java @@ -0,0 +1,28 @@ +package io.openems.edge.controller.api.backend.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.ComponentConfigRequestHandler; + +@Component(property = { "entry=" + AuthenticatedRequestHandler.ENTRY_POINT }) +public class BindingComponentConfigRequestHandler implements JsonApi { + + private final ComponentConfigRequestHandler componentConfigRequestHandler; + + @Activate + public BindingComponentConfigRequestHandler(// + @Reference ComponentConfigRequestHandler componentConfigRequestHandler // + ) { + this.componentConfigRequestHandler = componentConfigRequestHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.componentConfigRequestHandler.buildJsonApiRoutes(builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/BindingComponentRequestHandler.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/BindingComponentRequestHandler.java new file mode 100644 index 00000000000..58b91fd51ca --- /dev/null +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/BindingComponentRequestHandler.java @@ -0,0 +1,28 @@ +package io.openems.edge.controller.api.backend.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.ComponentRequestHandler; + +@Component(property = { "entry=" + AuthenticatedRequestHandler.ENTRY_POINT }) +public class BindingComponentRequestHandler implements JsonApi { + + private final ComponentRequestHandler componentRequestHandler; + + @Activate + public BindingComponentRequestHandler(// + @Reference ComponentRequestHandler componentRequestHandler // + ) { + this.componentRequestHandler = componentRequestHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.componentRequestHandler.buildJsonApiRoutes(builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/BindingRoutesJsonApiHandler.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/BindingRoutesJsonApiHandler.java new file mode 100644 index 00000000000..def81a37ab6 --- /dev/null +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/BindingRoutesJsonApiHandler.java @@ -0,0 +1,40 @@ +package io.openems.edge.controller.api.backend.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceScope; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.RoutesJsonApiHandler; + +@Component(// + property = { "entry=" + AuthenticatedRequestHandler.ENTRY_POINT }, // + service = { BindingRoutesJsonApiHandler.class, JsonApi.class } // +) +public class BindingRoutesJsonApiHandler implements JsonApi { + + private final RoutesJsonApiHandler jsonApiHandler; + + @Activate + public BindingRoutesJsonApiHandler(// + @Reference(scope = ReferenceScope.PROTOTYPE_REQUIRED) RoutesJsonApiHandler jsonApiHandler // + ) { + this.jsonApiHandler = jsonApiHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.jsonApiHandler.buildJsonApiRoutes(builder); + } + + public JsonApiBuilder getBuilder() { + return this.jsonApiHandler.getBuilder(); + } + + public void setBuilder(JsonApiBuilder builder) { + this.jsonApiHandler.setBuilder(builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/RootRequestHandler.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/RootRequestHandler.java new file mode 100644 index 00000000000..68f07c59e27 --- /dev/null +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/RootRequestHandler.java @@ -0,0 +1,52 @@ +package io.openems.edge.controller.api.backend.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.component.annotations.ServiceScope; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.jsonapi.MultipleJsonApiBinder; + +@Component(// + service = { RootRequestHandler.class, JsonApi.class }, // + scope = ServiceScope.SINGLETON // +) +public class RootRequestHandler implements JsonApi { + + public static final String ENTRY_POINT = "edge.backend.root"; + + private final MultipleJsonApiBinder binder = new MultipleJsonApiBinder(); + private final BindingRoutesJsonApiHandler routesHandler; + + @Reference(// + target = "(entry=" + ENTRY_POINT + ")", // + policyOption = ReferencePolicyOption.GREEDY, // + policy = ReferencePolicy.DYNAMIC, // + cardinality = ReferenceCardinality.MULTIPLE // + ) + protected void bindHandler(JsonApi handler) { + this.binder.bindJsonApi(handler); + this.routesHandler.setBuilder(this.binder.getJsonApiBuilder()); + } + + protected void unbindHandler(JsonApi handler) { + this.binder.unbindJsonApi(handler); + this.routesHandler.setBuilder(null); + } + + @Activate + public RootRequestHandler(@Reference BindingRoutesJsonApiHandler routesHandler) { + this.routesHandler = routesHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.addBuilder(this.binder.getJsonApiBuilder()); + } + +} diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/SubscribeSystemLogJsonApiHandler.java b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/SubscribeSystemLogJsonApiHandler.java new file mode 100644 index 00000000000..856bc85beaf --- /dev/null +++ b/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/handler/SubscribeSystemLogJsonApiHandler.java @@ -0,0 +1,63 @@ +package io.openems.edge.controller.api.backend.handler; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.ops4j.pax.logging.spi.PaxAppender; +import org.ops4j.pax.logging.spi.PaxLoggingEvent; +import org.osgi.service.component.annotations.Component; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.notification.SystemLogNotification; +import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest; +import io.openems.common.jsonrpc.response.AuthenticatedRpcResponse; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.backend.ControllerApiBackendImpl; +import io.openems.edge.controller.api.backend.WebsocketClient; + +@Component(property = { // + "entry=" + AuthenticatedRequestHandler.ENTRY_POINT, // + "org.ops4j.pax.logging.appender.name=Controller.Api.Backend", // +}) +public class SubscribeSystemLogJsonApiHandler implements JsonApi, PaxAppender { + + private final Set subscriber = ConcurrentHashMap.newKeySet(); + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(SubscribeSystemLogRequest.METHOD, call -> { + final var webSocket = call.get(ControllerApiBackendImpl.WEBSOCKET_CLIENT_KEY); + if (webSocket == null) { + throw new OpenemsException("Websocket is not defined."); + } + final var request = SubscribeSystemLogRequest.from(call.getRequest()); + if (request.isSubscribe()) { + this.subscriber.add(webSocket); + } else { + this.subscriber.remove(webSocket); + } + + return new AuthenticatedRpcResponse(call.getRequest().getId(), + new GenericJsonrpcResponseSuccess(request.getId())); + }); + } + + @Override + public void doAppend(PaxLoggingEvent event) { + if (this.subscriber.isEmpty()) { + return; + } + + final var notification = SystemLogNotification.fromPaxLoggingEvent(event); + final var iterator = this.subscriber.iterator(); + while (iterator.hasNext()) { + final var ws = iterator.next(); + if (!ws.sendMessage(notification)) { + iterator.remove(); + } + } + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/ControllerApiBackendImplTest.java b/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/ControllerApiBackendImplTest.java index d5d49a61b28..9d594615157 100644 --- a/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/ControllerApiBackendImplTest.java +++ b/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/ControllerApiBackendImplTest.java @@ -40,6 +40,7 @@ public void test() throws Exception { .addReference("componentManager", new DummyComponentManager(clock)) // .addReference("cycle", new DummyCycle(1000)) // .addReference("resendHistoricDataWorkerFactory", new DummyResendHistoricDataWorkerFactory()) // + .addReference("requestHandlerFactory", new DummyBackendOnRequestFactory()) // .addReference("oem", new DummyOpenemsEdgeOem()) // .addComponent(new DummySum()) // .activate(MyConfig.create() // diff --git a/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/DummyBackendOnRequestFactory.java b/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/DummyBackendOnRequestFactory.java new file mode 100644 index 00000000000..05aed052db3 --- /dev/null +++ b/io.openems.edge.controller.api.backend/test/io/openems/edge/controller/api/backend/DummyBackendOnRequestFactory.java @@ -0,0 +1,41 @@ +package io.openems.edge.controller.api.backend; + +import java.lang.reflect.InvocationTargetException; + +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentServiceObjects; + +import io.openems.common.utils.ReflectionUtils; +import io.openems.edge.controller.api.backend.handler.BindingRoutesJsonApiHandler; +import io.openems.edge.controller.api.backend.handler.RootRequestHandler; +import io.openems.edge.controller.api.common.handler.RoutesJsonApiHandler; + +public class DummyBackendOnRequestFactory extends BackendOnRequest.Factory { + + public DummyBackendOnRequestFactory() + throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + super(); + ReflectionUtils.setAttribute(BackendOnRequest.Factory.class, this, "cso", new DummyBackendOnRequestCso()); + } + + private static class DummyBackendOnRequestCso implements ComponentServiceObjects { + + @Override + public BackendOnRequest getService() { + return new BackendOnRequest( + new RootRequestHandler(new BindingRoutesJsonApiHandler(new RoutesJsonApiHandler()))); + } + + @Override + public void ungetService(BackendOnRequest service) { + // empty for tests + } + + @Override + public ServiceReference getServiceReference() { + // empty for tests + return null; + } + } + +} diff --git a/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/ComponentConfigRequestHandler.java b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/ComponentConfigRequestHandler.java new file mode 100644 index 00000000000..1eb5cb4f331 --- /dev/null +++ b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/ComponentConfigRequestHandler.java @@ -0,0 +1,87 @@ +package io.openems.edge.controller.api.common.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ServiceScope; + +import io.openems.common.jsonrpc.request.ComponentJsonApiRequest; +import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; +import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; +import io.openems.common.jsonrpc.request.GetEdgeConfigRequest; +import io.openems.common.jsonrpc.request.SetChannelValueRequest; +import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; +import io.openems.common.session.Role; +import io.openems.edge.common.component.ComponentManager; +import io.openems.edge.common.jsonapi.EdgeGuards; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.jsonapi.Key; +import io.openems.edge.controller.api.common.ApiWorker; + +@Component(service = { ComponentConfigRequestHandler.class, JsonApi.class }, scope = ServiceScope.SINGLETON) +public class ComponentConfigRequestHandler implements JsonApi { + + public static final Key API_WORKER_KEY = new Key<>("apiWorker", ApiWorker.class); + + private final ComponentManager componentManager; + + @Activate + public ComponentConfigRequestHandler(@Reference ComponentManager componentManager) { + this.componentManager = componentManager; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.delegate(GetEdgeConfigRequest.METHOD, endpoint -> { + endpoint.setDescription(""" + Handles a GetEdgeConfigRequest. + Delegates original request to a ComponentJsonApiRequest. + """); + }, call -> { + return new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, + GetEdgeConfigRequest.from(call.getRequest())); + }); + + builder.delegate(CreateComponentConfigRequest.METHOD, endpoint -> { + endpoint.setDescription(""" + Handles a CreateComponentConfigRequest. + Delegates original request to a ComponentJsonApiRequest. + """); + }, call -> { + return new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, + CreateComponentConfigRequest.from(call.getRequest())); + }); + + builder.delegate(UpdateComponentConfigRequest.METHOD, endpoint -> { + endpoint.setDescription(""" + Handles a UpdateComponentConfigRequest. + Delegates original request to a ComponentJsonApiRequest. + """); + }, call -> { + return new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, + UpdateComponentConfigRequest.from(call.getRequest())); + }); + + builder.delegate(DeleteComponentConfigRequest.METHOD, endpoint -> { + endpoint.setDescription(""" + Handles a DeleteComponentConfigRequest. + Delegates original request to a ComponentJsonApiRequest. + """); + }, call -> { + return new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, + DeleteComponentConfigRequest.from(call.getRequest())); + }); + + builder.handleRequest(SetChannelValueRequest.METHOD, endpoint -> { + endpoint.setDescription("Handles a SetChannelValueRequest") // + .setGuards(EdgeGuards.roleIsAtleast(Role.ADMIN)); + }, call -> { + final var apiWorker = call.get(API_WORKER_KEY); + return apiWorker.handleSetChannelValueRequest(this.componentManager, call.get(EdgeKeys.USER_KEY), + SetChannelValueRequest.from(call.getRequest())).get(); + }); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/ComponentRequestHandler.java b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/ComponentRequestHandler.java new file mode 100644 index 00000000000..bf04173b4c3 --- /dev/null +++ b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/ComponentRequestHandler.java @@ -0,0 +1,145 @@ +package io.openems.edge.controller.api.common.handler; + +import java.util.Map; +import java.util.TreeMap; +import java.util.function.Consumer; + +import org.osgi.framework.Constants; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.component.annotations.ServiceScope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.jsonrpc.request.ComponentJsonApiRequest; +import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.jsonapi.JsonApiEndpoint; +import io.openems.edge.common.jsonapi.Subrequest; +import io.openems.edge.common.jsonapi.Tag; + +/** + * This class handles {@link ComponentJsonApiRequest} and delegates the request + * to their component. + */ +@Component(service = { ComponentRequestHandler.class, JsonApi.class }, scope = ServiceScope.SINGLETON) +public class ComponentRequestHandler implements JsonApi { + + private record BoundComponentJsonApi(// + long serviceId, // + JsonApiBuilder apiBuilder, // + Consumer endpointListener // + ) { + + public ComponentRequestHandler.BoundComponentJsonApi with(Consumer endpointListener) { + return new BoundComponentJsonApi(this.serviceId, this.apiBuilder, endpointListener); + } + + } + + private final Logger log = LoggerFactory.getLogger(ComponentRequestHandler.class); + + private final Map jsonApis = new TreeMap<>(); + + /** + * Binds a {@link ComponentJsonApi}. + * + * @param jsonApi the {@link ComponentJsonApi} to bind + * @param ref the OSGi component properties + */ + @Reference(// + policy = ReferencePolicy.DYNAMIC, // + bind = "bindJsonApi", unbind = "unbindJsonApi", // + updated = "updateJsonApi", // + policyOption = ReferencePolicyOption.GREEDY, // + cardinality = ReferenceCardinality.MULTIPLE // + ) + public void bindJsonApi(ComponentJsonApi jsonApi, Map ref) { + final var builder = new JsonApiBuilder(); + + final var boundComponent = new BoundComponentJsonApi(// + (Long) ref.get(Constants.SERVICE_ID), // + builder, // + t -> t.getDef().getTags().add(new Tag(jsonApi.id())) // + ); + + builder.addEndpointAddedListener(boundComponent.endpointListener()); + jsonApi.buildJsonApiRoutes(builder); + this.jsonApis.put(jsonApi.id(), boundComponent); + this.log.info("Added '" + jsonApi.id() + "' to Component Apis."); + + } + + /** + * Updates a {@link ComponentJsonApi} on configuration change. + * + * @param jsonApi the {@link ComponentJsonApi} to update + * @param ref the updated OSGi component properties + */ + public void updateJsonApi(ComponentJsonApi jsonApi, Map ref) { + final long servicePid = (Long) ref.get(Constants.SERVICE_ID); + for (var entry : this.jsonApis.entrySet()) { + if (entry.getValue().serviceId() != servicePid) { + continue; + } + final var previousComponentId = entry.getKey(); + final var prevBinding = this.jsonApis.remove(previousComponentId); + for (var endpoint : prevBinding.apiBuilder().getEndpoints().values()) { + endpoint.getDef().getTags().removeIf(t -> t.name().equals(previousComponentId)); + endpoint.getDef().getTags().add(new Tag(jsonApi.id())); + } + prevBinding.apiBuilder().removeEndpointAddedListener(prevBinding.endpointListener()); + final var newBinding = prevBinding.with(t -> t.getDef().getTags().add(new Tag(jsonApi.id()))); + prevBinding.apiBuilder().addEndpointAddedListener(newBinding.endpointListener()); + + this.jsonApis.put(jsonApi.id(), newBinding); + break; + } + this.log.info("Updated Component Api " + jsonApi.id()); + } + + /** + * Unbinds a {@link ComponentJsonApi}. + * + * @param jsonApi the {@link ComponentJsonApi} to remove + * @param ref the updated OSGi component properties + */ + public void unbindJsonApi(ComponentJsonApi jsonApi, Map ref) { + this.jsonApis.remove(jsonApi.id()); + this.log.info("Removed '" + jsonApi.id() + "' from Component Apis."); + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.rpc(ComponentJsonApiRequest.METHOD, endpoint -> { + endpoint.setDescription("Handles a ComponentJsonApiRequest."); + + }, () -> { + return this.jsonApis.entrySet().stream() // + .map(jsonApiEntry -> { + final var subrequest = new Subrequest(JsonUtils.buildJsonObject() // + .addProperty("componentId", jsonApiEntry.getKey()) // + .build()); + + subrequest.addRpcBuilderFor(jsonApiEntry.getValue().apiBuilder(), "payload"); + return subrequest; + }).toList(); + }, call -> { + final var request = ComponentJsonApiRequest.from(call.getRequest()); + final var component = this.jsonApis.get(request.getComponentId()); + if (component == null) { + throw new RuntimeException("Component with id '" + request.getComponentId() + "' was not found"); + } + var mapped = call.mapRequest(request.getPayload()); + component.apiBuilder().handle(mapped); + + call.setResponse(mapped.getResponse()); + }); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/QueryRequestHandler.java b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/QueryRequestHandler.java new file mode 100644 index 00000000000..9bed397860b --- /dev/null +++ b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/QueryRequestHandler.java @@ -0,0 +1,73 @@ +package io.openems.edge.controller.api.common.handler; + +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesDataRequest; +import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesEnergyPerPeriodRequest; +import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesEnergyRequest; +import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesExportXlxsRequest; +import io.openems.common.jsonrpc.response.QueryHistoricTimeseriesDataResponse; +import io.openems.common.jsonrpc.response.QueryHistoricTimeseriesEnergyPerPeriodResponse; +import io.openems.common.jsonrpc.response.QueryHistoricTimeseriesEnergyResponse; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.timedata.api.Timedata; + +@Component(service = { QueryRequestHandler.class, JsonApi.class }) +public class QueryRequestHandler implements JsonApi { + + @Reference(// + policy = ReferencePolicy.DYNAMIC, // + policyOption = ReferencePolicyOption.GREEDY, // + cardinality = ReferenceCardinality.OPTIONAL // + ) + private volatile Timedata timedata; + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(QueryHistoricTimeseriesDataRequest.METHOD, call -> { + final var data = this.getTimedata().queryHistoricData(// + null, /* ignore Edge-ID */ + QueryHistoricTimeseriesDataRequest.from(call.getRequest())); + + return new QueryHistoricTimeseriesDataResponse(call.getRequest().getId(), data); + }); + + builder.handleRequest(QueryHistoricTimeseriesEnergyRequest.METHOD, call -> { + final var request = QueryHistoricTimeseriesEnergyRequest.from(call.getRequest()); + final var data = this.getTimedata().queryHistoricEnergy(// + null, /* ignore Edge-ID */ + request.getFromDate(), request.getToDate(), request.getChannels()); + return new QueryHistoricTimeseriesEnergyResponse(request.getId(), data); + }); + + builder.handleRequest(QueryHistoricTimeseriesEnergyPerPeriodRequest.METHOD, call -> { + final var request = QueryHistoricTimeseriesEnergyPerPeriodRequest.from(call.getRequest()); + var data = this.getTimedata().queryHistoricEnergyPerPeriod(// + null, /* ignore Edge-ID */ + request.getFromDate(), request.getToDate(), request.getChannels(), request.getResolution()); + return new QueryHistoricTimeseriesEnergyPerPeriodResponse(request.getId(), data); + }); + + builder.handleRequest(QueryHistoricTimeseriesExportXlxsRequest.METHOD, call -> { + final var request = QueryHistoricTimeseriesExportXlxsRequest.from(call.getRequest()); + return this.getTimedata().handleQueryHistoricTimeseriesExportXlxsRequest(null /* ignore Edge-ID */, request, + call.get(EdgeKeys.USER_KEY).getLanguage()); + }); + } + + private final Timedata getTimedata() throws OpenemsException { + final var currentTimedata = this.timedata; + if (currentTimedata == null) { + throw new OpenemsException("There is no Timedata-Service available!"); + } + return currentTimedata; + } + +} diff --git a/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/RoutesJsonApiHandler.java b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/RoutesJsonApiHandler.java new file mode 100644 index 00000000000..4cceb550d83 --- /dev/null +++ b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/RoutesJsonApiHandler.java @@ -0,0 +1,134 @@ +package io.openems.edge.controller.api.common.handler; + +import static io.openems.common.utils.JsonUtils.toJsonArray; +import static java.util.Collections.emptyList; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ServiceScope; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.jsonapi.JsonApiEndpoint; +import io.openems.edge.common.jsonapi.JsonrpcRoleEndpointGuard; +import io.openems.edge.common.jsonapi.Subrequest; +import io.openems.edge.common.jsonapi.Tag; + +@Component(// + service = { RoutesJsonApiHandler.class, JsonApi.class }, // + scope = ServiceScope.PROTOTYPE // +) +public class RoutesJsonApiHandler implements JsonApi { + + private JsonApiBuilder builder; + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest("routes", call -> { + final var b = this.getBuilder(); + if (b == null) { + throw new OpenemsException("Builder is not yet set."); + } + + final var result = getAllRequests(emptyList(), b); + + return new GenericJsonrpcResponseSuccess(call.getRequest().getId(), JsonUtils.buildJsonObject() // + .addProperty("version", "1") // + .add("endpoints", result.stream().collect(toJsonArray())) // + .build()); + }); + } + + public JsonApiBuilder getBuilder() { + return this.builder; + } + + public void setBuilder(JsonApiBuilder builder) { + this.builder = builder; + } + + private record EndpointParent(JsonApiEndpoint parentEndpoint, JsonElement baseRequest, String[] path) { + + } + + private static final List getAllRequests(List parent, JsonApiBuilder builder) { + final var result = new ArrayList(); + for (var entry : builder.getEndpoints().entrySet()) { + final var def = entry.getValue().getDef(); + + final var parentArray = parent.stream() // + .map(t -> JsonUtils.buildJsonObject() // + .addProperty("method", t.parentEndpoint().getMethod()) // + .add("request", JsonUtils.buildJsonObject() // + .add("base", t.baseRequest()) // + .add("pathToSubrequest", Stream.of(t.path()) // + .map(JsonPrimitive::new) // + .collect(toJsonArray())) // + .build()) + .build()) // + .collect(toJsonArray()); + + final var resultJson = JsonUtils.buildJsonObject() // + .addProperty("method", entry.getKey()) // + .addPropertyIfNotNull("description", def.getDescription()) // + .add("tags", def.getTags().stream() // + .map(Tag.serializer()::serialize) // + .collect(toJsonArray())) + .add("guards", def.getGuards().stream() // + .map(t -> { + if (t instanceof JsonrpcRoleEndpointGuard a) { + return JsonrpcRoleEndpointGuard.serializer().serialize(a); + } + return null; + }) // + .filter(Objects::nonNull) // + .collect(toJsonArray())) + .add("parent", parentArray); + + def.applyRequestBuilder(request -> { + resultJson.add("request", JsonUtils.buildJsonObject() // + .onlyIf(request.getSerializer() != null, t -> { + t.add("json", request.getSerializer().descriptor().toJson()) // + .add("examples", request.createExampleArray()); + }).build()); + }); + + def.applyResponseBuilder(response -> { + resultJson.add("response", JsonUtils.buildJsonObject() // + .onlyIf(response.getSerializer() != null, t -> { + t.add("json", response.getSerializer().descriptor().toJson()) // + .add("examples", response.createExampleArray()); + }).build()); + }); + + result.add(resultJson.build()); + + final List subroutes = entry.getValue().getSubroutes() != null + ? entry.getValue().getSubroutes().get() + : emptyList(); + + for (var subroute : subroutes) { + for (var b : subroute.getSubrouteToBuilder()) { + var currentParent = new EndpointParent(entry.getValue(), subroute.getBaseRequest(), b.path()); + final var subparents = new ArrayList<>(parent); + subparents.add(currentParent); + result.addAll(getAllRequests(subparents, b.builder())); + } + } + + } + return result; + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/package-info.java b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/package-info.java new file mode 100644 index 00000000000..26261007ee9 --- /dev/null +++ b/io.openems.edge.controller.api.common/src/io/openems/edge/controller/api/common/handler/package-info.java @@ -0,0 +1,3 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +@org.osgi.annotation.bundle.Export +package io.openems.edge.controller.api.common.handler; diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/AbstractModbusTcpApi.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/AbstractModbusTcpApi.java index 63246fe38c2..d862b6c7177 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/AbstractModbusTcpApi.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/AbstractModbusTcpApi.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.TreeMap; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import org.osgi.service.cm.ConfigurationAdmin; @@ -16,15 +15,15 @@ import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.utils.ConfigUtils; import io.openems.common.worker.AbstractWorker; import io.openems.edge.common.channel.Channel; import io.openems.edge.common.channel.WriteChannel; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.jsonapi.JsonrpcEndpointGuard; import io.openems.edge.common.meta.Meta; import io.openems.edge.common.modbusslave.ModbusRecord; import io.openems.edge.common.modbusslave.ModbusRecordChannel; @@ -34,7 +33,6 @@ import io.openems.edge.common.modbusslave.ModbusRecordUint16Hash; import io.openems.edge.common.modbusslave.ModbusSlave; import io.openems.edge.common.modbusslave.ModbusSlaveNatureTable; -import io.openems.edge.common.user.User; import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.api.common.ApiWorker; import io.openems.edge.controller.api.common.WritePojo; @@ -44,7 +42,7 @@ import io.openems.edge.controller.api.modbus.jsonrpc.GetModbusProtocolResponse; public abstract class AbstractModbusTcpApi extends AbstractOpenemsComponent - implements ModbusTcpApi, Controller, OpenemsComponent, JsonApi { + implements ModbusTcpApi, Controller, OpenemsComponent, ComponentJsonApi { public static final int UNIT_ID = 1; public static final int DEFAULT_PORT = 502; @@ -377,25 +375,33 @@ protected void logWarn(Logger log, String message) { } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest message) - throws OpenemsNamedException { - if (this.getComponentMissingFault().get() == Boolean.TRUE) { - throw new OpenemsException(this.getComponentMissingFaultChannel().channelDoc().getText()); - } - if (this.getComponentNoModbusApiFault().get() == Boolean.TRUE) { - throw new OpenemsException(this.getComponentNoModbusApiFaultChannel().channelDoc().getText()); - } - - switch (message.getMethod()) { - case GetModbusProtocolRequest.METHOD: - return CompletableFuture.completedFuture(new GetModbusProtocolResponse(message.getId(), this.records)); + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(GetModbusProtocolRequest.METHOD, def -> { + def.setGuards(this.componentMissingGuard(), this.componentNoModbusApiGuard()); + }, call -> { + return new GetModbusProtocolResponse(call.getRequest().getId(), this.records); + }); + builder.handleRequest(GetModbusProtocolExportXlsxRequest.METHOD, def -> { + def.setGuards(this.componentMissingGuard(), this.componentNoModbusApiGuard()); + }, call -> { + return new GetModbusProtocolExportXlsxResponse(call.getRequest().getId(), this.components, this.records); + }); + } - case GetModbusProtocolExportXlsxRequest.METHOD: - return CompletableFuture.completedFuture( - new GetModbusProtocolExportXlsxResponse(message.getId(), this.components, this.records)); + private JsonrpcEndpointGuard componentMissingGuard() { + return call -> { + if (this.getComponentMissingFault().get() == Boolean.TRUE) { + throw new OpenemsException(this.getComponentMissingFaultChannel().channelDoc().getText()); + } + }; + } - } - return null; + private JsonrpcEndpointGuard componentNoModbusApiGuard() { + return call -> { + if (this.getComponentNoModbusApiFault().get() == Boolean.TRUE) { + throw new OpenemsException(this.getComponentNoModbusApiFaultChannel().channelDoc().getText()); + } + }; } /** diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/readonly/ControllerApiModbusTcpReadOnlyImpl.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/readonly/ControllerApiModbusTcpReadOnlyImpl.java index 187c6548c7d..b983cbd8df4 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/readonly/ControllerApiModbusTcpReadOnlyImpl.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/readonly/ControllerApiModbusTcpReadOnlyImpl.java @@ -17,7 +17,7 @@ import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.ComponentJsonApi; import io.openems.edge.common.meta.Meta; import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.api.modbus.AbstractModbusTcpApi; @@ -30,7 +30,7 @@ configurationPolicy = ConfigurationPolicy.REQUIRE // ) public class ControllerApiModbusTcpReadOnlyImpl extends AbstractModbusTcpApi - implements ControllerApiModbusTcpReadOnly, ModbusTcpApi, Controller, OpenemsComponent, JsonApi { + implements ControllerApiModbusTcpReadOnly, ModbusTcpApi, Controller, OpenemsComponent, ComponentJsonApi { @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) private Meta metaComponent = null; diff --git a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/readwrite/ControllerApiModbusTcpReadWriteImpl.java b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/readwrite/ControllerApiModbusTcpReadWriteImpl.java index a29416d1146..af51ee0b092 100644 --- a/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/readwrite/ControllerApiModbusTcpReadWriteImpl.java +++ b/io.openems.edge.controller.api.modbus/src/io/openems/edge/controller/api/modbus/readwrite/ControllerApiModbusTcpReadWriteImpl.java @@ -17,7 +17,7 @@ import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsException; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.ComponentJsonApi; import io.openems.edge.common.meta.Meta; import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.api.modbus.AbstractModbusTcpApi; @@ -30,7 +30,7 @@ configurationPolicy = ConfigurationPolicy.REQUIRE // ) public class ControllerApiModbusTcpReadWriteImpl extends AbstractModbusTcpApi - implements ControllerApiModbusTcpReadWrite, ModbusTcpApi, Controller, OpenemsComponent, JsonApi { + implements ControllerApiModbusTcpReadWrite, ModbusTcpApi, Controller, OpenemsComponent, ComponentJsonApi { @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) private Meta metaComponent = null; diff --git a/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/Config.java b/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/Config.java index 7d101ef1f60..8e3655d6555 100644 --- a/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/Config.java +++ b/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/Config.java @@ -23,6 +23,9 @@ @AttributeDefinition(name = "Edge-ID", description = "Client-ID for authentication at MQTT broker") String clientId() default "edge0"; + @AttributeDefinition(name = "Topic prefix", description = "Optional topic prefix (/edge//...)") + String topicPrefix() default ""; + @AttributeDefinition(name = "Username", description = "Username for authentication at MQTT broker") String username(); diff --git a/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/ControllerApiMqtt.java b/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/ControllerApiMqtt.java index 8ba29e82c37..e470466555e 100644 --- a/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/ControllerApiMqtt.java +++ b/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/ControllerApiMqtt.java @@ -8,7 +8,6 @@ public interface ControllerApiMqtt extends Controller, OpenemsComponent, EventHandler { - public static final String TOPIC_PREFIX = "edge/%s/"; public static final String TOPIC_CHANNEL_PREFIX = "channel/"; public static final String TOPIC_CHANNEL_LAST_UPDATE = "lastUpdate"; public static final String TOPIC_EDGE_CONFIG = "edgeConfig/"; diff --git a/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/ControllerApiMqttImpl.java b/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/ControllerApiMqttImpl.java index 14b355d8f99..7707612270a 100644 --- a/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/ControllerApiMqttImpl.java +++ b/io.openems.edge.controller.api.mqtt/src/io/openems/edge/controller/api/mqtt/ControllerApiMqttImpl.java @@ -74,7 +74,7 @@ private void activate(ComponentContext context, Config config) throws Exception this.config = config; // Publish MQTT messages under the topic "edge/edge0/..." - this.topicPrefix = String.format(ControllerApiMqtt.TOPIC_PREFIX, config.clientId()); + this.topicPrefix = createTopicPrefix(config); super.activate(context, config.id(), config.alias(), config.enabled()); this.mqttConnector.connect(config.uri(), config.clientId(), config.username(), config.password(), @@ -84,6 +84,31 @@ private void activate(ComponentContext context, Config config) throws Exception }); } + /** + * Creates the topic prefix in either format. + * + *

+ * + * @param config the {@link Config} + * @return the prefix + */ + protected static String createTopicPrefix(Config config) { + final var b = new StringBuilder(); + if (config.topicPrefix() != null && !config.topicPrefix().isBlank()) { + b // + .append(config.topicPrefix()) // + .append("/"); + } + b // + .append("edge/") // + .append(config.clientId()) // + .append("/"); + return b.toString(); + } + @Override @Deactivate protected void deactivate() { diff --git a/io.openems.edge.controller.api.mqtt/test/io/openems/edge/controller/api/mqtt/ControllerApiMqttImplTest.java b/io.openems.edge.controller.api.mqtt/test/io/openems/edge/controller/api/mqtt/ControllerApiMqttImplTest.java index 515d91ef207..10981834b04 100644 --- a/io.openems.edge.controller.api.mqtt/test/io/openems/edge/controller/api/mqtt/ControllerApiMqttImplTest.java +++ b/io.openems.edge.controller.api.mqtt/test/io/openems/edge/controller/api/mqtt/ControllerApiMqttImplTest.java @@ -1,5 +1,8 @@ package io.openems.edge.controller.api.mqtt; +import static io.openems.edge.controller.api.mqtt.ControllerApiMqttImpl.createTopicPrefix; +import static org.junit.Assert.assertEquals; + import java.time.Instant; import java.time.ZoneOffset; @@ -25,6 +28,7 @@ public void test() throws Exception { .activate(MyConfig.create() // .setId(CTRL_ID) // .setClientId("edge0") // + .setTopicPrefix("") // .setUsername("guest") // .setPassword("guest") // .setUri("ws://localhost:1883") // @@ -36,4 +40,19 @@ public void test() throws Exception { .build()); } + @Test + public void testCreateTopicPrefix() throws Exception { + assertEquals("foo/bar/edge/edge0/", createTopicPrefix(MyConfig.create() // + .setClientId("edge0") // + .setTopicPrefix("foo/bar") // + .build())); + assertEquals("edge/edge0/", createTopicPrefix(MyConfig.create() // + .setClientId("edge0") // + .setTopicPrefix("") // + .build())); + assertEquals("edge/edge0/", createTopicPrefix(MyConfig.create() // + .setClientId("edge0") // + .setTopicPrefix(null) // + .build())); + } } \ No newline at end of file diff --git a/io.openems.edge.controller.api.mqtt/test/io/openems/edge/controller/api/mqtt/MyConfig.java b/io.openems.edge.controller.api.mqtt/test/io/openems/edge/controller/api/mqtt/MyConfig.java index aa2c382fba8..51e98901e37 100644 --- a/io.openems.edge.controller.api.mqtt/test/io/openems/edge/controller/api/mqtt/MyConfig.java +++ b/io.openems.edge.controller.api.mqtt/test/io/openems/edge/controller/api/mqtt/MyConfig.java @@ -12,6 +12,7 @@ protected static class Builder { private PersistencePriority persistencePriority; private boolean debugMode; private String clientId; + private String topicPrefix; private String username; private String password; private String certPem; @@ -36,6 +37,11 @@ public Builder setClientId(String clientId) { return this; } + public Builder setTopicPrefix(String topicPrefix) { + this.topicPrefix = topicPrefix; + return this; + } + public Builder setUsername(String username) { this.username = username; return this; @@ -112,6 +118,11 @@ public String clientId() { return this.builder.clientId; } + @Override + public String topicPrefix() { + return this.builder.topicPrefix; + } + @Override public String username() { return this.builder.username; diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/AbstractRestApi.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/AbstractRestApi.java index 6bdc7228e31..db1d16228f7 100644 --- a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/AbstractRestApi.java +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/AbstractRestApi.java @@ -12,15 +12,14 @@ import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.user.UserService; import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.api.common.ApiWorker; +import io.openems.edge.controller.api.common.handler.ComponentConfigRequestHandler; import io.openems.edge.controller.api.rest.readonly.ControllerApiRestReadOnlyImpl; -import io.openems.edge.timedata.api.Timedata; public abstract class AbstractRestApi extends AbstractOpenemsComponent implements RestApi, Controller, OpenemsComponent { @@ -83,6 +82,10 @@ protected void activate(ComponentContext context, String id, String alias, boole "Unable to start " + this.implementationName + " on port [" + port + "]: " + e.getMessage()); this._setUnableToStart(true); } + + this.getRpcRestHandler().setOnCall(call -> { + call.put(ComponentConfigRequestHandler.API_WORKER_KEY, this.apiWorker); + }); } @Override @@ -122,14 +125,6 @@ protected boolean isDebugModeEnabled() { return this.isDebugModeEnabled; } - /** - * Gets the Timedata service. - * - * @return the service - * @throws OpenemsException if the timeservice is not available - */ - protected abstract Timedata getTimedata() throws OpenemsException; - /** * Gets the UserService. * @@ -144,6 +139,13 @@ protected boolean isDebugModeEnabled() { */ protected abstract ComponentManager getComponentManager(); + /** + * Gets the JsonRpcRestHandler. + * + * @return the service + */ + protected abstract JsonRpcRestHandler getRpcRestHandler(); + /** * Gets the AccessMode. * diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/JsonRpcRestHandler.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/JsonRpcRestHandler.java new file mode 100644 index 00000000000..0421bfa8a49 --- /dev/null +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/JsonRpcRestHandler.java @@ -0,0 +1,109 @@ +package io.openems.edge.controller.api.rest; + +import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; + +import org.osgi.service.component.ComponentServiceObjects; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.component.annotations.ServiceScope; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.utils.FunctionUtils; +import io.openems.edge.common.jsonapi.Call; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.SingleJsonApiBinder; +import io.openems.edge.common.user.User; +import io.openems.edge.controller.api.rest.handler.RootRequestHandler; + +@Component(// + scope = ServiceScope.PROTOTYPE, // + service = JsonRpcRestHandler.class // +) +public class JsonRpcRestHandler { + + @Component(service = JsonRpcRestHandler.Factory.class) + public static class Factory { + + @Reference + private ComponentServiceObjects cso; + + /** + * Returns a new {@link JsonRpcRestHandler} service object. + * + * @return the created {@link JsonRpcRestHandler} object + * @see #unget(JsonRpcRestHandler) + */ + public JsonRpcRestHandler get() { + return this.cso.getService(); + } + + /** + * Releases the {@link JsonRpcRestHandler} service object. + * + * @param service a {@link JsonRpcRestHandler} provided by this factory + * @see #get() + */ + public void unget(JsonRpcRestHandler service) { + if (service == null) { + return; + } + this.cso.ungetService(service); + } + + } + + private final SingleJsonApiBinder apiBinder = new SingleJsonApiBinder(); + + private Consumer> onCall = FunctionUtils::doNothing; + + /** + * Binds the {@link RootRequestHandler}. + * + * @param rootHandler the handler + */ + @Reference(// + cardinality = ReferenceCardinality.OPTIONAL, // + policy = ReferencePolicy.DYNAMIC, // + policyOption = ReferencePolicyOption.GREEDY // + ) + public void bindRootHandler(RootRequestHandler rootHandler) { + this.apiBinder.bind(rootHandler); + } + + /** + * Unbinds the {@link RootRequestHandler}. + * + * @param rootHandler the handler + */ + public void unbindRootHandler(RootRequestHandler rootHandler) { + this.apiBinder.unbind(); + } + + /** + * Handles a rest request. + * + * @param user the user of the current request + * @param request the request to handle + * @return the result future + * @throws OpenemsNamedException on error + */ + public CompletableFuture handleRequest(User user, JsonrpcRequest request) + throws OpenemsNamedException { + return this.apiBinder.handleRequest(request, call -> { + call.put(EdgeKeys.USER_KEY, user); + this.onCall.accept(call); + }); + } + + public void setOnCall(Consumer> onCall) { + this.onCall = onCall == null ? FunctionUtils::doNothing : onCall; + } + +} diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestHandler.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestHandler.java index b5650ea5fde..1819afa7696 100644 --- a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestHandler.java +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/RestHandler.java @@ -1,20 +1,18 @@ package io.openems.edge.controller.api.rest; -import java.io.BufferedReader; +import static io.openems.common.utils.JsonUtils.parseToJsonObject; +import static java.util.stream.Collectors.joining; + import java.io.IOException; -import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.Base64; import java.util.List; -import java.util.Map; import java.util.StringTokenizer; import java.util.UUID; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import java.util.stream.Collectors; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; @@ -24,36 +22,23 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import io.openems.common.channel.AccessMode; import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; import io.openems.common.jsonrpc.base.JsonrpcMessage; import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseError; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; -import io.openems.common.jsonrpc.request.ComponentJsonApiRequest; -import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; -import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; -import io.openems.common.jsonrpc.request.GetEdgeConfigRequest; -import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesDataRequest; -import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesEnergyRequest; import io.openems.common.jsonrpc.request.SetChannelValueRequest; -import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; -import io.openems.common.jsonrpc.response.QueryHistoricTimeseriesDataResponse; -import io.openems.common.jsonrpc.response.QueryHistoricTimeseriesEnergyResponse; import io.openems.common.session.Role; import io.openems.common.types.ChannelAddress; import io.openems.common.utils.JsonUtils; import io.openems.common.utils.StringUtils; import io.openems.common.utils.UuidUtils; import io.openems.edge.common.channel.Channel; -import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; import io.openems.edge.common.user.User; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -364,11 +349,12 @@ private boolean handlePost(User user, ChannelAddress channelAddress, Request bas */ private static JsonObject parseJson(Request baseRequest) throws OpenemsException { try { - return JsonParser.parseString(// - new BufferedReader(new InputStreamReader(baseRequest.getInputStream())) // - .lines() // - .collect(Collectors.joining("\n"))) // - .getAsJsonObject(); + try (var br = baseRequest.getReader()) { + return parseToJsonObject(br // + .lines() // + .collect(joining("\n"))); + } + } catch (Exception e) { throw new OpenemsException("Unable to parse: " + e.getMessage()); } @@ -432,7 +418,7 @@ private void handleJsonRpc(User user, Request baseRequest, HttpServletRequest ht requestId = request.getId(); // handle the request - var responseFuture = this.handleJsonRpcRequest(user, request); + var responseFuture = this.parent.getRpcRestHandler().handleRequest(user, request); // wait for response JsonrpcResponseSuccess response; @@ -453,194 +439,4 @@ private void handleJsonRpc(User user, Request baseRequest, HttpServletRequest ht } } - /** - * Handles an JSON-RPC Request. - * - * @param user the {@link User} - * @param request the {@link JsonrpcRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsException on error - */ - private CompletableFuture handleJsonRpcRequest(User user, JsonrpcRequest request) - throws OpenemsException, OpenemsNamedException { - switch (request.getMethod()) { - - case QueryHistoricTimeseriesDataRequest.METHOD: - return this.handleQueryHistoricDataRequest(user, QueryHistoricTimeseriesDataRequest.from(request)); - - case QueryHistoricTimeseriesEnergyRequest.METHOD: - return this.handleQueryHistoricEnergyRequest(user, QueryHistoricTimeseriesEnergyRequest.from(request)); - - case GetEdgeConfigRequest.METHOD: - return this.handleGetEdgeConfigRequest(user, GetEdgeConfigRequest.from(request)); - - case CreateComponentConfigRequest.METHOD: - return this.handleCreateComponentConfigRequest(user, CreateComponentConfigRequest.from(request)); - - case UpdateComponentConfigRequest.METHOD: - return this.handleUpdateComponentConfigRequest(user, UpdateComponentConfigRequest.from(request)); - - case DeleteComponentConfigRequest.METHOD: - return this.handleDeleteComponentConfigRequest(user, DeleteComponentConfigRequest.from(request)); - - case ComponentJsonApiRequest.METHOD: - return this.handleComponentJsonApiRequest(user, ComponentJsonApiRequest.from(request)); - - default: - this.parent.logWarn(this.log, "Unhandled Request: " + request); - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } - } - - /** - * Handles a QueryHistoricDataRequest. - * - * @param user the {@link User} - * @param request the {@link QueryHistoricTimeseriesDataRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleQueryHistoricDataRequest(User user, - QueryHistoricTimeseriesDataRequest request) throws OpenemsNamedException { - var data = this.parent.getTimedata().queryHistoricData(// - null, /* ignore Edge-ID */ - request); - - // JSON-RPC response - return CompletableFuture.completedFuture(new QueryHistoricTimeseriesDataResponse(request.getId(), data)); - } - - /** - * Handles a QueryHistoricEnergyRequest. - * - * @param user the {@link User} - * @param request the {@link QueryHistoricTimeseriesEnergyRequest} - * @return the Future JSPN-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleQueryHistoricEnergyRequest(User user, - QueryHistoricTimeseriesEnergyRequest request) throws OpenemsNamedException { - Map data = this.parent.getTimedata().queryHistoricEnergy(// - null, /* ignore Edge-ID */ - request.getFromDate(), request.getToDate(), request.getChannels()); - - // JSON-RPC response - return CompletableFuture.completedFuture(new QueryHistoricTimeseriesEnergyResponse(request.getId(), data)); - } - - /** - * Handles a GetEdgeConfigRequest. - * - * @param user the {@link User} - * @param getEdgeConfigRequest the {@link GetEdgeConfigRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleGetEdgeConfigRequest(User user, - GetEdgeConfigRequest getEdgeConfigRequest) throws OpenemsNamedException { - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, getEdgeConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a CreateComponentConfigRequest. - * - * @param user the {@link User} - * @param createComponentConfigRequest the {@link CreateComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleCreateComponentConfigRequest(User user, - CreateComponentConfigRequest createComponentConfigRequest) throws OpenemsNamedException { - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, - createComponentConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a UpdateComponentConfigRequest. - * - * @param user the {@link User} - * @param updateComponentConfigRequest the {@link UpdateComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleUpdateComponentConfigRequest(User user, - UpdateComponentConfigRequest updateComponentConfigRequest) throws OpenemsNamedException { - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, - updateComponentConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a DeleteComponentConfigRequest. - * - * @param user the User - * @param deleteComponentConfigRequest the DeleteComponentConfigRequest - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleDeleteComponentConfigRequest(User user, - DeleteComponentConfigRequest deleteComponentConfigRequest) throws OpenemsNamedException { - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, - deleteComponentConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a ComponentJsonApiRequest. - * - * @param user the User - * @param request the ComponentJsonApiRequest - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleComponentJsonApiRequest(User user, - ComponentJsonApiRequest request) throws OpenemsNamedException { - // get Component - var componentId = request.getComponentId(); - var component = this.parent.getComponentManager().getComponent(componentId); - - if (component == null) { - throw new OpenemsException("Unable to find Component [" + componentId + "]"); - } - - if (!(component instanceof JsonApi)) { - throw new OpenemsException("Component [" + componentId + "] is no JsonApi"); - } - - // call JsonApi - var jsonApi = (JsonApi) component; - CompletableFuture responseFuture = jsonApi.handleJsonrpcRequest(user, - request.getPayload()); - - // handle null response - if (responseFuture == null) { - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getPayload().getMethod()); - } - - // Wrap reply in EdgeRpcResponse - var edgeRpcResponse = new CompletableFuture(); - responseFuture.whenComplete((r, ex) -> { - if (ex != null) { - edgeRpcResponse.completeExceptionally(ex); - } else if (r != null) { - edgeRpcResponse.complete(new GenericJsonrpcResponseSuccess(request.getId(), r.getResult())); - } else { - edgeRpcResponse.completeExceptionally(new OpenemsNamedException(OpenemsError.JSONRPC_UNHANDLED_METHOD, - request.getPayload().getMethod())); - } - }); - - return edgeRpcResponse; - } - } diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingComponentConfigRequestHandler.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingComponentConfigRequestHandler.java new file mode 100644 index 00000000000..00c85ca9887 --- /dev/null +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingComponentConfigRequestHandler.java @@ -0,0 +1,34 @@ +package io.openems.edge.controller.api.rest.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.ComponentConfigRequestHandler; + +/** + * This class makes it possible to request component update, delete, ... + * {@link ComponentConfigRequestHandler} in a websocket connection. It just + * "binds" the component which handles the request and provides their methods + * indirectly. + */ +@Component(property = "entry=" + RootRequestHandler.ENTRY_POINT) +public class BindingComponentConfigRequestHandler implements JsonApi { + + private final ComponentConfigRequestHandler componentConfigRequestHandler; + + @Activate + public BindingComponentConfigRequestHandler(// + @Reference ComponentConfigRequestHandler componentConfigRequestHandler // + ) { + this.componentConfigRequestHandler = componentConfigRequestHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.componentConfigRequestHandler.buildJsonApiRoutes(builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingComponentRequestHandler.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingComponentRequestHandler.java new file mode 100644 index 00000000000..318b0806154 --- /dev/null +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingComponentRequestHandler.java @@ -0,0 +1,34 @@ +package io.openems.edge.controller.api.rest.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import io.openems.common.jsonrpc.request.ComponentJsonApiRequest; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.ComponentRequestHandler; + +/** + * This class makes it possible to request {@link ComponentJsonApiRequest} in a + * websocket connection. It just "binds" the component which handles the request + * and provides their methods indirectly. + */ +@Component(property = "entry=" + RootRequestHandler.ENTRY_POINT) +public class BindingComponentRequestHandler implements JsonApi { + + private final ComponentRequestHandler componentRequestHandler; + + @Activate + public BindingComponentRequestHandler(// + @Reference ComponentRequestHandler componentRequestHandler // + ) { + this.componentRequestHandler = componentRequestHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.componentRequestHandler.buildJsonApiRoutes(builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingQueryRequestHandler.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingQueryRequestHandler.java new file mode 100644 index 00000000000..6dc37c078ae --- /dev/null +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingQueryRequestHandler.java @@ -0,0 +1,26 @@ +package io.openems.edge.controller.api.rest.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.QueryRequestHandler; + +@Component(property = "entry=" + RootRequestHandler.ENTRY_POINT) +public class BindingQueryRequestHandler implements JsonApi { + + private final QueryRequestHandler queryRequestHandler; + + @Activate + public BindingQueryRequestHandler(@Reference QueryRequestHandler handler) { + this.queryRequestHandler = handler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.queryRequestHandler.buildJsonApiRoutes(builder); + } + +} diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingRoutesJsonApiHandler.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingRoutesJsonApiHandler.java new file mode 100644 index 00000000000..8d683db4614 --- /dev/null +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/BindingRoutesJsonApiHandler.java @@ -0,0 +1,42 @@ +package io.openems.edge.controller.api.rest.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceScope; +import org.osgi.service.component.annotations.ServiceScope; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.RoutesJsonApiHandler; + +@Component(// + property = "entry=" + RootRequestHandler.ENTRY_POINT, // + service = { BindingRoutesJsonApiHandler.class, JsonApi.class }, // + scope = ServiceScope.SINGLETON // +) +public class BindingRoutesJsonApiHandler implements JsonApi { + + private final RoutesJsonApiHandler jsonApiHandler; + + @Activate + public BindingRoutesJsonApiHandler(// + @Reference(scope = ReferenceScope.PROTOTYPE_REQUIRED) RoutesJsonApiHandler jsonApiHandler // + ) { + this.jsonApiHandler = jsonApiHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.jsonApiHandler.buildJsonApiRoutes(builder); + } + + public JsonApiBuilder getBuilder() { + return this.jsonApiHandler.getBuilder(); + } + + public void setBuilder(JsonApiBuilder builder) { + this.jsonApiHandler.setBuilder(builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/RootRequestHandler.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/RootRequestHandler.java new file mode 100644 index 00000000000..33e3558af1f --- /dev/null +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/handler/RootRequestHandler.java @@ -0,0 +1,55 @@ +package io.openems.edge.controller.api.rest.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.jsonapi.MultipleJsonApiBinder; + +@Component(service = { RootRequestHandler.class, JsonApi.class }) +public class RootRequestHandler implements JsonApi { + + public static final String ENTRY_POINT = "edge.rest.root"; + + private final MultipleJsonApiBinder binder = new MultipleJsonApiBinder(); + + /** + * Binds a {@link JsonApi2}. + * + * @param jsonApi the {@link JsonApi2} to bind + */ + @Reference(// + target = "(entry=" + ENTRY_POINT + ")", // + cardinality = ReferenceCardinality.MULTIPLE, // + policy = ReferencePolicy.DYNAMIC, // + policyOption = ReferencePolicyOption.GREEDY // + ) + public void bindJsonApi(JsonApi jsonApi) { + this.binder.bindJsonApi(jsonApi); + } + + /** + * Unbinds a {@link JsonApi2}. + * + * @param jsonApi the {@link JsonApi2} to unbind + */ + public void unbindJsonApi(JsonApi jsonApi) { + this.binder.unbindJsonApi(jsonApi); + } + + @Activate + public RootRequestHandler(@Reference BindingRoutesJsonApiHandler routesJsonApiHandler) { + routesJsonApiHandler.setBuilder(this.binder.getJsonApiBuilder()); + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.addBuilder(this.binder.getJsonApiBuilder()); + } + +} diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/readonly/ControllerApiRestReadOnlyImpl.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/readonly/ControllerApiRestReadOnlyImpl.java index 4595f7f1f35..39841518748 100644 --- a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/readonly/ControllerApiRestReadOnlyImpl.java +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/readonly/ControllerApiRestReadOnlyImpl.java @@ -6,9 +6,6 @@ import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; -import org.osgi.service.component.annotations.ReferencePolicy; -import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.metatype.annotations.Designate; import io.openems.common.channel.AccessMode; @@ -18,8 +15,8 @@ import io.openems.edge.common.user.UserService; import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.api.rest.AbstractRestApi; +import io.openems.edge.controller.api.rest.JsonRpcRestHandler; import io.openems.edge.controller.api.rest.RestApi; -import io.openems.edge.timedata.api.Timedata; @Designate(ocd = Config.class, factory = true) @Component(// @@ -30,15 +27,16 @@ public class ControllerApiRestReadOnlyImpl extends AbstractRestApi implements ControllerApiRestReadOnly, RestApi, Controller, OpenemsComponent { + @Reference + private JsonRpcRestHandler.Factory restHandlerFactory; + private JsonRpcRestHandler restHandler; + @Reference private ComponentManager componentManager; @Reference private UserService userService; - @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) - private volatile Timedata timedata = null; - public ControllerApiRestReadOnlyImpl() { super("REST-Api Read-Only", // OpenemsComponent.ChannelId.values(), // @@ -50,6 +48,8 @@ public ControllerApiRestReadOnlyImpl() { @Activate private void activate(ComponentContext context, Config config) throws OpenemsException { + this.restHandler = this.restHandlerFactory.get(); + super.activate(context, config.id(), config.alias(), config.enabled(), config.debugMode(), 0, /* no timeout */ config.port(), config.connectionlimit()); } @@ -58,14 +58,7 @@ private void activate(ComponentContext context, Config config) throws OpenemsExc @Deactivate protected void deactivate() { super.deactivate(); - } - - @Override - protected Timedata getTimedata() throws OpenemsException { - if (this.timedata != null) { - return this.timedata; - } - throw new OpenemsException("There is no Timedata-Service available!"); + this.restHandlerFactory.unget(this.restHandler); } @Override @@ -78,6 +71,11 @@ protected ComponentManager getComponentManager() { return this.componentManager; } + @Override + protected JsonRpcRestHandler getRpcRestHandler() { + return this.restHandler; + } + @Override protected AccessMode getAccessMode() { return AccessMode.READ_ONLY; diff --git a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/readwrite/ControllerApiRestReadWriteImpl.java b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/readwrite/ControllerApiRestReadWriteImpl.java index 9eed3c74014..2faef0d16a2 100644 --- a/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/readwrite/ControllerApiRestReadWriteImpl.java +++ b/io.openems.edge.controller.api.rest/src/io/openems/edge/controller/api/rest/readwrite/ControllerApiRestReadWriteImpl.java @@ -6,9 +6,6 @@ import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; -import org.osgi.service.component.annotations.ReferencePolicy; -import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.metatype.annotations.Designate; import io.openems.common.channel.AccessMode; @@ -18,8 +15,8 @@ import io.openems.edge.common.user.UserService; import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.api.rest.AbstractRestApi; +import io.openems.edge.controller.api.rest.JsonRpcRestHandler; import io.openems.edge.controller.api.rest.RestApi; -import io.openems.edge.timedata.api.Timedata; @Designate(ocd = Config.class, factory = true) @Component(// @@ -30,15 +27,16 @@ public class ControllerApiRestReadWriteImpl extends AbstractRestApi implements ControllerApiRestReadWrite, RestApi, Controller, OpenemsComponent { + @Reference + private JsonRpcRestHandler.Factory restHandlerFactory; + private JsonRpcRestHandler restHandler; + @Reference private ComponentManager componentManager; @Reference private UserService userService; - @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) - private volatile Timedata timedata = null; - public ControllerApiRestReadWriteImpl() { super("REST-Api Read-Write", // OpenemsComponent.ChannelId.values(), // @@ -51,6 +49,8 @@ public ControllerApiRestReadWriteImpl() { @Activate private void activate(ComponentContext context, Config config) throws OpenemsException { + this.restHandler = this.restHandlerFactory.get(); + super.activate(context, config.id(), config.alias(), config.enabled(), config.debugMode(), config.apiTimeout(), config.port(), config.connectionlimit()); } @@ -59,14 +59,7 @@ private void activate(ComponentContext context, Config config) throws OpenemsExc @Deactivate protected void deactivate() { super.deactivate(); - } - - @Override - protected Timedata getTimedata() throws OpenemsException { - if (this.timedata != null) { - return this.timedata; - } - throw new OpenemsException("There is no Timedata-Service available!"); + this.restHandlerFactory.unget(this.restHandler); } @Override @@ -79,6 +72,11 @@ protected ComponentManager getComponentManager() { return this.componentManager; } + @Override + protected JsonRpcRestHandler getRpcRestHandler() { + return this.restHandler; + } + @Override protected AccessMode getAccessMode() { return AccessMode.READ_WRITE; diff --git a/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/DummyJsonRpcRestHandlerFactory.java b/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/DummyJsonRpcRestHandlerFactory.java new file mode 100644 index 00000000000..2b99bdb06c3 --- /dev/null +++ b/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/DummyJsonRpcRestHandlerFactory.java @@ -0,0 +1,47 @@ +package io.openems.edge.controller.api.rest; + +import java.lang.reflect.InvocationTargetException; + +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentServiceObjects; + +import com.google.common.base.Supplier; + +import io.openems.common.utils.ReflectionUtils; + +public class DummyJsonRpcRestHandlerFactory extends JsonRpcRestHandler.Factory { + + public DummyJsonRpcRestHandlerFactory(Supplier factoryMethod) + throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + super(); + ReflectionUtils.setAttribute(JsonRpcRestHandler.Factory.class, this, "cso", + new DummyJsonRpcRestHandlerCso(factoryMethod)); + } + + private static class DummyJsonRpcRestHandlerCso implements ComponentServiceObjects { + + private final Supplier factoryMethod; + + public DummyJsonRpcRestHandlerCso(Supplier factoryMethod) { + super(); + this.factoryMethod = factoryMethod; + } + + @Override + public JsonRpcRestHandler getService() { + return this.factoryMethod.get(); + } + + @Override + public void ungetService(JsonRpcRestHandler service) { + // empty for tests + } + + @Override + public ServiceReference getServiceReference() { + // empty for tests + return null; + } + } + +} diff --git a/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/readonly/ControllerApiRestReadOnlyImplTest.java b/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/readonly/ControllerApiRestReadOnlyImplTest.java index 6b71381c73a..e7bc5a974ef 100644 --- a/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/readonly/ControllerApiRestReadOnlyImplTest.java +++ b/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/readonly/ControllerApiRestReadOnlyImplTest.java @@ -6,8 +6,9 @@ import io.openems.edge.common.test.DummyComponentManager; import io.openems.edge.common.test.DummyUserService; import io.openems.edge.common.test.TestUtils; +import io.openems.edge.controller.api.rest.DummyJsonRpcRestHandlerFactory; +import io.openems.edge.controller.api.rest.JsonRpcRestHandler; import io.openems.edge.controller.test.ControllerTest; -import io.openems.edge.timedata.test.DummyTimedata; public class ControllerApiRestReadOnlyImplTest { @@ -20,7 +21,7 @@ public void test() throws OpenemsException, Exception { new ControllerTest(new ControllerApiRestReadOnlyImpl()) // .addReference("componentManager", new DummyComponentManager()) // .addReference("userService", new DummyUserService()) // - .addReference("timedata", new DummyTimedata("timedata0")) // + .addReference("restHandlerFactory", new DummyJsonRpcRestHandlerFactory(JsonRpcRestHandler::new)) // .activate(MyConfig.create() // .setId(CTRL_ID) // .setEnabled(false) // do not actually start server diff --git a/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/readwrite/ControllerApiRestReadWriteImplTest.java b/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/readwrite/ControllerApiRestReadWriteImplTest.java index d118133f257..6baf712a911 100644 --- a/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/readwrite/ControllerApiRestReadWriteImplTest.java +++ b/io.openems.edge.controller.api.rest/test/io/openems/edge/controller/api/rest/readwrite/ControllerApiRestReadWriteImplTest.java @@ -15,7 +15,9 @@ import java.util.Base64; import org.junit.Test; +import org.osgi.framework.Constants; +import com.google.common.collect.ImmutableMap; import com.google.gson.JsonElement; import com.google.gson.JsonObject; @@ -36,8 +38,16 @@ import io.openems.edge.common.test.DummyComponentManager; import io.openems.edge.common.test.DummyUserService; import io.openems.edge.common.test.TestUtils; +import io.openems.edge.controller.api.common.handler.ComponentConfigRequestHandler; +import io.openems.edge.controller.api.common.handler.ComponentRequestHandler; +import io.openems.edge.controller.api.common.handler.RoutesJsonApiHandler; +import io.openems.edge.controller.api.rest.DummyJsonRpcRestHandlerFactory; +import io.openems.edge.controller.api.rest.JsonRpcRestHandler; +import io.openems.edge.controller.api.rest.handler.BindingComponentConfigRequestHandler; +import io.openems.edge.controller.api.rest.handler.BindingComponentRequestHandler; +import io.openems.edge.controller.api.rest.handler.BindingRoutesJsonApiHandler; +import io.openems.edge.controller.api.rest.handler.RootRequestHandler; import io.openems.edge.controller.test.ControllerTest; -import io.openems.edge.timedata.test.DummyTimedata; public class ControllerApiRestReadWriteImplTest { @@ -48,12 +58,29 @@ public class ControllerApiRestReadWriteImplTest { public void test() throws OpenemsException, Exception { final var port = TestUtils.findRandomOpenPortOnAllLocalInterfaces(); + final var componentManager = new DummyComponentManager(); + + final var rootHandler = new RootRequestHandler(new BindingRoutesJsonApiHandler(new RoutesJsonApiHandler())); + rootHandler.bindJsonApi( + new BindingComponentConfigRequestHandler(new ComponentConfigRequestHandler(componentManager))); + final var componentRequestHandler = new ComponentRequestHandler(); + componentRequestHandler.bindJsonApi(componentManager, ImmutableMap.builder() // + .put(Constants.SERVICE_ID, 0L) // + .build()); + rootHandler.bindJsonApi(new BindingComponentRequestHandler(componentRequestHandler)); + + final var factory = new DummyJsonRpcRestHandlerFactory(() -> { + final var restHandler = new JsonRpcRestHandler(); + restHandler.bindRootHandler(rootHandler); + return restHandler; + }); + var sut = new ControllerApiRestReadWriteImpl(); var test = new ControllerTest(sut) // - .addReference("componentManager", new DummyComponentManager()) // + .addReference("componentManager", componentManager) // .addReference("userService", new DummyUserService(// DUMMY_GUEST, DUMMY_OWNER, DUMMY_INSTALLER, DUMMY_ADMIN)) // - .addReference("timedata", new DummyTimedata("timedata0")) // + .addReference("restHandlerFactory", factory) // .addComponent(new DummyComponent(DUMMY_ID) // .withReadChannel(1234)) // .activate(MyConfig.create() // diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/Config.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/Config.java index cc861c598f6..40a10f7fd72 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/Config.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/Config.java @@ -23,5 +23,8 @@ @AttributeDefinition(name = "Api-Timeout", description = "Sets the timeout in seconds for updates on Channels set by this Api.") int apiTimeout() default 60; + @AttributeDefinition(name = "Debug Mode", description = "Activates the debug mode") + boolean debugMode() default false; + String webconsole_configurationFactory_nameHint() default "Controller Api Websocket [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/ControllerApiWebsocket.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/ControllerApiWebsocket.java index f8113b044dd..33d5eec61e8 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/ControllerApiWebsocket.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/ControllerApiWebsocket.java @@ -1,6 +1,5 @@ package io.openems.edge.controller.api.websocket; -import org.ops4j.pax.logging.spi.PaxAppender; import org.osgi.service.event.EventHandler; import io.openems.common.channel.Level; @@ -8,7 +7,7 @@ import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.controller.api.Controller; -public interface ControllerApiWebsocket extends Controller, OpenemsComponent, PaxAppender, EventHandler { +public interface ControllerApiWebsocket extends Controller, OpenemsComponent, EventHandler { public static final String EDGE_ID = "0"; public static final String EDGE_COMMENT = ""; diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/ControllerApiWebsocketImpl.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/ControllerApiWebsocketImpl.java index fdb68329a49..f352756454f 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/ControllerApiWebsocketImpl.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/ControllerApiWebsocketImpl.java @@ -1,22 +1,14 @@ package io.openems.edge.controller.api.websocket; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import org.java_websocket.WebSocket; -import org.ops4j.pax.logging.spi.PaxAppender; -import org.ops4j.pax.logging.spi.PaxLoggingEvent; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; -import org.osgi.service.component.annotations.ReferenceCardinality; -import org.osgi.service.component.annotations.ReferencePolicy; -import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.event.Event; import org.osgi.service.event.EventHandler; import org.osgi.service.event.propertytypes.EventTopics; @@ -25,12 +17,9 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder; -import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.notification.EdgeConfigNotification; import io.openems.common.jsonrpc.notification.EdgeRpcNotification; -import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest; import io.openems.common.types.EdgeConfig; import io.openems.common.utils.ThreadPoolUtils; import io.openems.common.websocket.AbstractWebsocketServer.DebugMode; @@ -38,55 +27,48 @@ import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; -import io.openems.edge.common.user.User; import io.openems.edge.common.user.UserService; import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.api.common.ApiWorker; -import io.openems.edge.timedata.api.Timedata; +import io.openems.edge.controller.api.common.handler.ComponentConfigRequestHandler; @Designate(ocd = Config.class, factory = true) @Component(// name = "Controller.Api.Websocket", // immediate = true, // - configurationPolicy = ConfigurationPolicy.REQUIRE, // - property = { // - "org.ops4j.pax.logging.appender.name=Controller.Api.Websocket" // - }) + configurationPolicy = ConfigurationPolicy.REQUIRE // +) @EventTopics({ // EdgeEventConstants.TOPIC_CONFIG_UPDATE, // EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // }) public class ControllerApiWebsocketImpl extends AbstractOpenemsComponent - implements ControllerApiWebsocket, Controller, OpenemsComponent, PaxAppender, EventHandler { + implements ControllerApiWebsocket, Controller, OpenemsComponent, EventHandler { private static final int POOL_SIZE = 10; - /** Stores valid session tokens for authentication via Cookie. */ - protected final Map sessionTokens = new ConcurrentHashMap<>(); protected final ApiWorker apiWorker = new ApiWorker(this); - private final SystemLogHandler systemLogHandler; - @Reference protected ComponentManager componentManager; @Reference protected UserService userService; - @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) - private volatile Timedata timedata = null; - protected WebsocketServer server = null; private ScheduledExecutorService executor; + @Reference + protected OnRequest.Factory onRequestFactory; + protected OnRequest onRequest; + public ControllerApiWebsocketImpl() { super(// OpenemsComponent.ChannelId.values(), // Controller.ChannelId.values(), // ControllerApiWebsocket.ChannelId.values() // ); - this.systemLogHandler = new SystemLogHandler(this); } @Activate @@ -103,7 +85,14 @@ private void activate(ComponentContext context, Config config) { new ThreadFactoryBuilder().setNameFormat(name + "-%d").build()); this.apiWorker.setTimeoutSeconds(config.apiTimeout()); + + this.onRequest = this.onRequestFactory.get(); + this.onRequest.setOnCall(call -> { + call.put(ComponentConfigRequestHandler.API_WORKER_KEY, this.apiWorker); + }); + this.onRequest.setDebug(config.debugMode()); this.startServer(config.port(), POOL_SIZE, DebugMode.OFF); + } @Override @@ -111,6 +100,7 @@ private void activate(ComponentContext context, Config config) { protected void deactivate() { super.deactivate(); this.stopServer(); + this.onRequestFactory.unget(this.onRequest); ThreadPoolUtils.shutdownAndAwaitTermination(this.executor, 5); } @@ -155,43 +145,6 @@ protected void logError(Logger log, String message) { super.logError(log, message); } - @Override - public void doAppend(PaxLoggingEvent event) { - this.systemLogHandler.handlePaxLoggingEvent(event); - } - - /** - * Gets the WebSocket connection attachment for a UI token. - * - * @param token the UI token - * @return the WsData - * @throws OpenemsNamedException if there is no connection with this token - */ - protected WsData getWsDataForTokenOrError(String token) throws OpenemsNamedException { - var connections = this.server.getConnections(); - for (WebSocket websocket : connections) { - WsData wsData = websocket.getAttachment(); - var thisToken = wsData.getSessionToken(); - if (thisToken != null && thisToken.equals(token)) { - return wsData; - } - } - throw OpenemsError.BACKEND_NO_UI_WITH_TOKEN.exception(token); - } - - /** - * Handles a SubscribeSystemLogRequest by forwarding it to the - * 'SystemLogHandler'. - * - * @param token the UI token - * @param request the SubscribeSystemLogRequest - * @throws OpenemsNamedException on error - */ - protected void handleSubscribeSystemLogRequest(String token, SubscribeSystemLogRequest request) - throws OpenemsNamedException { - this.systemLogHandler.handleSubscribeSystemLogRequest(token, request); - } - @Override public void handleEvent(Event event) { if (!this.isEnabled()) { @@ -217,16 +170,4 @@ public void handleEvent(Event event) { } } - /** - * Gets the Timedata service. - * - * @return the service - * @throws OpenemsException if the timeservice is not available - */ - public Timedata getTimedata() throws OpenemsException { - if (this.timedata != null) { - return this.timedata; - } - throw new OpenemsException("There is no Timedata-Service available!"); - } } diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java index 7a398aa9bb5..66f4cd88d34 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/OnRequest.java @@ -1,534 +1,100 @@ package io.openems.edge.controller.api.websocket; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.function.Consumer; import org.java_websocket.WebSocket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.osgi.service.component.ComponentServiceObjects; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.component.annotations.ServiceScope; -import com.google.gson.JsonElement; - -import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; -import io.openems.common.jsonrpc.request.AuthenticateWithPasswordRequest; -import io.openems.common.jsonrpc.request.AuthenticateWithTokenRequest; -import io.openems.common.jsonrpc.request.ComponentJsonApiRequest; -import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; -import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; -import io.openems.common.jsonrpc.request.EdgeRpcRequest; -import io.openems.common.jsonrpc.request.GetEdgeConfigRequest; -import io.openems.common.jsonrpc.request.GetEdgeRequest; -import io.openems.common.jsonrpc.request.GetEdgesRequest; -import io.openems.common.jsonrpc.request.LogoutRequest; -import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesDataRequest; -import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesEnergyPerPeriodRequest; -import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesEnergyRequest; -import io.openems.common.jsonrpc.request.QueryHistoricTimeseriesExportXlxsRequest; -import io.openems.common.jsonrpc.request.SetChannelValueRequest; -import io.openems.common.jsonrpc.request.SubscribeChannelsRequest; -import io.openems.common.jsonrpc.request.SubscribeEdgesRequest; -import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest; -import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; -import io.openems.common.jsonrpc.response.AuthenticateResponse; -import io.openems.common.jsonrpc.response.EdgeRpcResponse; -import io.openems.common.jsonrpc.response.GetEdgeResponse; -import io.openems.common.jsonrpc.response.GetEdgesResponse; -import io.openems.common.jsonrpc.response.QueryHistoricTimeseriesDataResponse; -import io.openems.common.jsonrpc.response.QueryHistoricTimeseriesEnergyPerPeriodResponse; -import io.openems.common.jsonrpc.response.QueryHistoricTimeseriesEnergyResponse; -import io.openems.common.session.Language; -import io.openems.common.session.Role; -import io.openems.common.types.ChannelAddress; -import io.openems.edge.common.component.ComponentManager; -import io.openems.edge.common.jsonapi.JsonApi; -import io.openems.edge.common.user.User; - +import io.openems.common.utils.FunctionUtils; +import io.openems.edge.common.jsonapi.Call; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.Key; +import io.openems.edge.common.jsonapi.SingleJsonApiBinder; +import io.openems.edge.controller.api.websocket.handler.RootRequestHandler; + +@Component(service = { OnRequest.class }, scope = ServiceScope.PROTOTYPE) public class OnRequest implements io.openems.common.websocket.OnRequest { - private final Logger log = LoggerFactory.getLogger(OnRequest.class); - private final ControllerApiWebsocketImpl parent; - - public OnRequest(ControllerApiWebsocketImpl parent) { - this.parent = parent; - } + @Component(service = OnRequest.Factory.class) + public static class Factory { - @Override - public CompletableFuture run(WebSocket ws, JsonrpcRequest request) - throws OpenemsException, OpenemsNamedException { - // get websocket attachment - WsData wsData = ws.getAttachment(); + @Reference + private ComponentServiceObjects cso; - // Start with authentication requests - switch (request.getMethod()) { - case AuthenticateWithTokenRequest.METHOD: - return this.handleAuthenticateWithTokenRequest(wsData, AuthenticateWithTokenRequest.from(request)); - - case AuthenticateWithPasswordRequest.METHOD: - return this.handleAuthenticateWithPasswordRequest(wsData, AuthenticateWithPasswordRequest.from(request)); + /** + * Returns a new {@link OnRequest} service object. + * + * @return the created {@link OnRequest} object + * @see #unget(OnRequest) + */ + public OnRequest get() { + return this.cso.getService(); } - // is user authenticated? - var user = wsData.assertUserIsAuthenticated(request.getMethod()); - user.assertRoleIsAtLeast(request.getMethod(), Role.GUEST); - - switch (request.getMethod()) { - case LogoutRequest.METHOD: - return this.handleLogoutRequest(wsData, user, LogoutRequest.from(request)); - - case EdgeRpcRequest.METHOD: - return this.handleEdgeRpcRequest(wsData, user, EdgeRpcRequest.from(request)); - - case GetEdgesRequest.METHOD: - return handleGetEdgesRequest(user, GetEdgesRequest.from(request)); - - case GetEdgeRequest.METHOD: - return handleGetEdgeRequest(user, GetEdgeRequest.from(request)); - - case SubscribeEdgesRequest.METHOD: - return this.handleSubscribeEdgesReqeust(user, SubscribeEdgesRequest.from(request)); - - default: - this.parent.logWarn(this.log, "Unhandled Request: " + request); - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } - } - - /** - * Handles a {@link LogoutRequest}. - * - * @param wsData the WebSocket attachment - * @param user the authenticated {@link User} - * @param request the {@link LogoutRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleLogoutRequest(WsData wsData, User user, - LogoutRequest request) throws OpenemsNamedException { - this.parent.sessionTokens.remove(wsData.getSessionToken(), user); - wsData.logout(); - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); - } - - /** - * Handles a {@link EdgeRpcRequest}. - * - * @param wsData the WebSocket attachment - * @param edgeRpcRequest the EdgeRpcRequest - * @param user the {@link User} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleEdgeRpcRequest(WsData wsData, User user, - EdgeRpcRequest edgeRpcRequest) throws OpenemsNamedException { - var request = edgeRpcRequest.getPayload(); - - CompletableFuture resultFuture; - switch (request.getMethod()) { - - case SubscribeChannelsRequest.METHOD: - resultFuture = this.handleSubscribeChannelsRequest(wsData, user, SubscribeChannelsRequest.from(request)); - break; - - case SubscribeSystemLogRequest.METHOD: - resultFuture = this.handleSubscribeSystemLogRequest(wsData, user, SubscribeSystemLogRequest.from(request)); - break; - - case QueryHistoricTimeseriesDataRequest.METHOD: - resultFuture = this.handleQueryHistoricDataRequest(user, QueryHistoricTimeseriesDataRequest.from(request)); - break; - - case QueryHistoricTimeseriesEnergyRequest.METHOD: - resultFuture = this.handleQueryHistoricEnergyRequest(QueryHistoricTimeseriesEnergyRequest.from(request)); - break; - - case QueryHistoricTimeseriesEnergyPerPeriodRequest.METHOD: - resultFuture = this.handleQueryHistoricEnergyPerPeriodRequest( - QueryHistoricTimeseriesEnergyPerPeriodRequest.from(request)); - break; - - case QueryHistoricTimeseriesExportXlxsRequest.METHOD: - resultFuture = this.handleQueryHistoricTimeseriesExportXlxsRequest(user, - QueryHistoricTimeseriesExportXlxsRequest.from(request)); - break; - - case CreateComponentConfigRequest.METHOD: - resultFuture = this.handleCreateComponentConfigRequest(user, CreateComponentConfigRequest.from(request)); - break; - - case UpdateComponentConfigRequest.METHOD: - resultFuture = this.handleUpdateComponentConfigRequest(user, UpdateComponentConfigRequest.from(request)); - break; - - case DeleteComponentConfigRequest.METHOD: - resultFuture = this.handleDeleteComponentConfigRequest(user, DeleteComponentConfigRequest.from(request)); - break; - - case GetEdgeConfigRequest.METHOD: - resultFuture = this.handleGetEdgeConfigRequest(user, GetEdgeConfigRequest.from(request)); - break; - - case SetChannelValueRequest.METHOD: - resultFuture = this.handleSetChannelValueRequest(user, SetChannelValueRequest.from(request)); - break; - - case ComponentJsonApiRequest.METHOD: - resultFuture = this.handleComponentJsonApiRequest(user, ComponentJsonApiRequest.from(request)); - break; - - // TODO: to be implemented: UI Logout - - default: - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } - - // Wrap reply in EdgeRpcResponse - var result = new CompletableFuture(); - resultFuture.whenComplete((r, ex) -> { - if (ex != null) { - result.completeExceptionally(ex); - } else if (r != null) { - result.complete(new EdgeRpcResponse(edgeRpcRequest.getId(), r)); - } else { - result.completeExceptionally( - new OpenemsNamedException(OpenemsError.JSONRPC_UNHANDLED_METHOD, request.getMethod())); + /** + * Releases the {@link OnRequest} service object. + * + * @param service a {@link OnRequest} provided by this factory + * @see #get() + */ + public void unget(OnRequest service) { + if (service == null) { + return; } - }); - return result; - } - - /** - * Handles a {@link AuthenticateWithTokenRequest}. - * - * @param wsData the WebSocket attachment - * @param request the {@link AuthenticateWithTokenRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleAuthenticateWithTokenRequest(WsData wsData, - AuthenticateWithTokenRequest request) throws OpenemsNamedException { - var token = request.getToken(); - return this.handleAuthentication(wsData, request.getId(), - Optional.ofNullable(this.parent.sessionTokens.get(token)), token); - } - - /** - * Handles a {@link AuthenticateWithPasswordRequest}. - * - * @param wsData the WebSocket attachment - * @param request the {@link AuthenticateWithPasswordRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleAuthenticateWithPasswordRequest(WsData wsData, - AuthenticateWithPasswordRequest request) throws OpenemsNamedException { - return this.handleAuthentication(wsData, request.getId(), - this.parent.userService.authenticate(request.password), UUID.randomUUID().toString()); - } - - /** - * Common handler for {@link AuthenticateWithTokenRequest} and - * {@link AuthenticateWithPasswordRequest}. - * - * @param wsData the WebSocket attachment - * @param requestId the ID of the original {@link JsonrpcRequest} - * @param userOpt the optional {@link User} - * @param token the existing or new token - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleAuthentication(WsData wsData, UUID requestId, - Optional userOpt, String token) throws OpenemsNamedException { - if (userOpt.isPresent()) { - var user = userOpt.get(); - wsData.setSessionToken(token); - wsData.setUser(user); - this.parent.sessionTokens.put(token, user); - this.parent.logInfo(this.log, "User [" + user.getId() + ":" + user.getName() + "] connected."); - - return CompletableFuture.completedFuture(new AuthenticateResponse(requestId, token, user, - List.of(Utils.getEdgeMetadata(user.getRole())), Language.DEFAULT)); + this.cso.ungetService(service); } - wsData.unsetUser(); - throw OpenemsError.COMMON_AUTHENTICATION_FAILED.exception(); - } - /** - * Handles a {@link SubscribeChannelsRequest}. - * - * @param wsData the WebSocket attachment - * @param user the {@link User} - * @param request the {@link SubscribeChannelsRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleSubscribeChannelsRequest(WsData wsData, User user, - SubscribeChannelsRequest request) throws OpenemsNamedException { - // Register subscription in WsData - wsData.handleSubscribeChannelsRequest(request); - - // JSON-RPC response - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); } - /** - * Handles a {@link QueryHistoricTimeseriesDataRequest}. - * - * @param user the {@link User} - * @param request the {@link QueryHistoricTimeseriesDataRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleQueryHistoricDataRequest(User user, - QueryHistoricTimeseriesDataRequest request) throws OpenemsNamedException { - var data = this.parent.getTimedata().queryHistoricData(// - null, /* ignore Edge-ID */ - request); - - // JSON-RPC response - return CompletableFuture.completedFuture(new QueryHistoricTimeseriesDataResponse(request.getId(), data)); - } + public static final Key WS_DATA_KEY = new Key<>("wsData", WsData.class); + public static final Key WEBSOCKET_KEY = new Key<>("websocket", WebSocket.class); - /** - * Handles a QueryHistoricEnergyRequest. - * - * @param request the QueryHistoricEnergyRequest - * @return the Future JSPN-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleQueryHistoricEnergyRequest( - QueryHistoricTimeseriesEnergyRequest request) throws OpenemsNamedException { - Map data = this.parent.getTimedata().queryHistoricEnergy(// - null, /* ignore Edge-ID */ - request.getFromDate(), request.getToDate(), request.getChannels()); - - // JSON-RPC response - return CompletableFuture.completedFuture(new QueryHistoricTimeseriesEnergyResponse(request.getId(), data)); - } - - /** - * Handles a {@link QueryHistoricTimeseriesEnergyPerPeriodRequest}. - * - * @param request the {@link QueryHistoricTimeseriesEnergyPerPeriodRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleQueryHistoricEnergyPerPeriodRequest( - QueryHistoricTimeseriesEnergyPerPeriodRequest request) throws OpenemsNamedException { - var data = this.parent.getTimedata().queryHistoricEnergyPerPeriod(// - null, /* ignore Edge-ID */ - request.getFromDate(), request.getToDate(), request.getChannels(), request.getResolution()); - - return CompletableFuture - .completedFuture(new QueryHistoricTimeseriesEnergyPerPeriodResponse(request.getId(), data)); - } + private final SingleJsonApiBinder apiBinder = new SingleJsonApiBinder(); + private Consumer> onCall = FunctionUtils::doNothing; - /** - * Handles a {@link QueryHistoricTimeseriesExportXlxsRequest}. - * - * @param user the {@link User} - * @param request the {@link QueryHistoricTimeseriesExportXlxsRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleQueryHistoricTimeseriesExportXlxsRequest(User user, - QueryHistoricTimeseriesExportXlxsRequest request) throws OpenemsNamedException { - return CompletableFuture.completedFuture( - this.parent.getTimedata().handleQueryHistoricTimeseriesExportXlxsRequest(null /* ignore Edge-ID */, - request, user.getLanguage())); + @Reference(// + cardinality = ReferenceCardinality.OPTIONAL, // + policy = ReferencePolicy.DYNAMIC, // + policyOption = ReferencePolicyOption.GREEDY // + ) + protected void bindRootHandler(RootRequestHandler rootHandler) { + this.apiBinder.bind(rootHandler); } - /** - * Handles a {@link CreateComponentConfigRequest}. - * - * @param user the {@link User} - * @param createComponentConfigRequest the {@link CreateComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleCreateComponentConfigRequest(User user, - CreateComponentConfigRequest createComponentConfigRequest) throws OpenemsNamedException { - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, - createComponentConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a {@link UpdateComponentConfigRequest}. - * - * @param user the {@link User} - * @param updateComponentConfigRequest the {@link UpdateComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleUpdateComponentConfigRequest(User user, - UpdateComponentConfigRequest updateComponentConfigRequest) throws OpenemsNamedException { - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, - updateComponentConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a {@link DeleteComponentConfigRequest}. - * - * @param user the {@link User} - * @param deleteComponentConfigRequest the {@link DeleteComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleDeleteComponentConfigRequest(User user, - DeleteComponentConfigRequest deleteComponentConfigRequest) throws OpenemsNamedException { - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, - deleteComponentConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); - } - - /** - * Handles a {@link GetEdgeConfigRequest}. - * - * @param user the {@link User} - * @param getEdgeConfigRequest the {@link GetEdgeConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleGetEdgeConfigRequest(User user, - GetEdgeConfigRequest getEdgeConfigRequest) throws OpenemsNamedException { - // wrap original request inside ComponentJsonApiRequest - var request = new ComponentJsonApiRequest(ComponentManager.SINGLETON_COMPONENT_ID, getEdgeConfigRequest); - - return this.handleComponentJsonApiRequest(user, request); + protected void unbindRootHandler(RootRequestHandler rootHandler) { + this.apiBinder.unbind(); } - /** - * Handles a {@link SetChannelValueRequest}. - * - * @param user the User - * @param request the {@link SetChannelValueRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleSetChannelValueRequest(User user, - SetChannelValueRequest request) throws OpenemsNamedException { - user.assertRoleIsAtLeast(SetChannelValueRequest.METHOD, Role.ADMIN); - - return this.parent.apiWorker.handleSetChannelValueRequest(this.parent.componentManager, user, request); - } - - /** - * Handles a {@link ComponentJsonApiRequest}. - * - * @param user the User - * @param request the {@link ComponentJsonApiRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleComponentJsonApiRequest(User user, - ComponentJsonApiRequest request) throws OpenemsNamedException { - // get Component - var componentId = request.getComponentId(); - var component = this.parent.componentManager.getComponent(componentId); - - if (component == null) { - throw new OpenemsException("Unable to find Component [" + componentId + "]"); - } - - if (!(component instanceof JsonApi)) { - throw new OpenemsException("Component [" + componentId + "] is no JsonApi"); - } - - // call JsonApi - var jsonApi = (JsonApi) component; - CompletableFuture responseFuture = jsonApi.handleJsonrpcRequest(user, - request.getPayload()); - - // handle null response - if (responseFuture == null) { - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getPayload().getMethod()); - } - - // Wrap reply in new JsonrpcResponseSuccess - var jsonrpcResponse = new CompletableFuture(); - responseFuture.whenComplete((r, ex) -> { - if (ex != null) { - jsonrpcResponse.completeExceptionally(ex); - } else if (r != null) { - jsonrpcResponse.complete(new GenericJsonrpcResponseSuccess(request.getId(), r.getResult())); - } else { - jsonrpcResponse.completeExceptionally(new OpenemsNamedException(OpenemsError.JSONRPC_UNHANDLED_METHOD, - request.getPayload().getMethod())); - } + @Override + public CompletableFuture run(WebSocket ws, JsonrpcRequest request) + throws OpenemsNamedException { + return this.apiBinder.handleRequest(request, call -> { + WsData wsData = ws.getAttachment(); + call.put(WS_DATA_KEY, wsData); + wsData.getUser().ifPresent(user -> { + call.put(EdgeKeys.USER_KEY, user); + }); + this.onCall.accept(call); }); - - return jsonrpcResponse; - } - - /** - * Handles a {@link SubscribeSystemLogRequest}. - * - * @param wsData the WebSocket attachment - * @param user the {@link User} - * @param request the {@link SubscribeSystemLogRequest} - * @return the JSON-RPC Success Response Future - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleSubscribeSystemLogRequest(WsData wsData, User user, - SubscribeSystemLogRequest request) throws OpenemsNamedException { - var token = wsData.getSessionToken(); - if (token == null) { - throw OpenemsError.BACKEND_UI_TOKEN_MISSING.exception(); - } - this.parent.handleSubscribeSystemLogRequest(token, request); - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); - } - - /** - * Handles a {@link GetEdgesRequest}. - * - * @param user the {@link User} - * @param request the {@link GetEdgesRequest} - * @return the {@link GetEdgesResponse} Response Future - * @throws OpenemsNamedException on error - */ - protected static CompletableFuture handleGetEdgesRequest(User user, GetEdgesRequest request) { - return CompletableFuture.completedFuture(// - new GetEdgesResponse(request.getId(), List.of(Utils.getEdgeMetadata(user.getGlobalRole())))); } - /** - * Handles a {@link GetEdgeRequest}. - * - * @param user the {@link User} - * @param request the {@link GetEdgeRequest} - * @return the {@link GetEdgeResponse} Response Future - */ - protected static CompletableFuture handleGetEdgeRequest(User user, GetEdgeRequest request) { - return CompletableFuture.completedFuture(// - new GetEdgeResponse(request.id, Utils.getEdgeMetadata(user.getGlobalRole()))); + public void setOnCall(Consumer> callAction) { + this.onCall = callAction == null ? FunctionUtils::doNothing : callAction; } - /** - * Handles a {@link SubscribeEdgesRequest}. - * - * @param user the {@link User} - * @param request the {@link SubscribeEdgesRequest} - * @return the Response Future - */ - private CompletableFuture handleSubscribeEdgesReqeust(User user, - SubscribeEdgesRequest request) { - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); + public void setDebug(boolean debug) { + this.apiBinder.setDebug(debug); } } diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/SystemLogHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/SystemLogHandler.java deleted file mode 100644 index 2e8ae3379e1..00000000000 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/SystemLogHandler.java +++ /dev/null @@ -1,78 +0,0 @@ -package io.openems.edge.controller.api.websocket; - -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - -import org.ops4j.pax.logging.spi.PaxLoggingEvent; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; -import io.openems.common.jsonrpc.notification.EdgeRpcNotification; -import io.openems.common.jsonrpc.notification.SystemLogNotification; -import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest; - -public class SystemLogHandler { - - private final Logger log = LoggerFactory.getLogger(SystemLogHandler.class); - private final ControllerApiWebsocketImpl parent; - private final Set subscriptions = new HashSet<>(); - - public SystemLogHandler(ControllerApiWebsocketImpl parent) { - this.parent = parent; - } - - /** - * Handles a {@link SubscribeSystemLogRequest}. - * - * @param token the UI session token - * @param request the {@link SubscribeSystemLogRequest} - * @return a reply - * @throws OpenemsNamedException on error - */ - public CompletableFuture handleSubscribeSystemLogRequest(String token, - SubscribeSystemLogRequest request) throws OpenemsNamedException { - if (request.isSubscribe()) { - /* - * Start subscription - */ - this.subscriptions.add(token); - - } else { - /* - * End subscription - */ - this.subscriptions.remove(token); - } - // announce success - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); - } - - /** - * Handles a PaxLoggingEvent and sends a SystemLogNotification to all subscribed - * UI sessions. - * - * @param event the event - */ - public void handlePaxLoggingEvent(PaxLoggingEvent event) { - synchronized (this.subscriptions) { - if (this.subscriptions.isEmpty()) { - return; - } - var notification = new EdgeRpcNotification(ControllerApiWebsocket.EDGE_ID, - SystemLogNotification.fromPaxLoggingEvent(event)); - for (var iter = this.subscriptions.iterator(); iter.hasNext();) { - var token = iter.next(); - try { - this.parent.getWsDataForTokenOrError(token).send(notification); - } catch (OpenemsNamedException e) { - iter.remove(); - this.log.warn("Unable to handle PaxLoggingEvent: " + e.getMessage()); - } - } - } - } -} diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketServer.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketServer.java index 0744f152e2e..cc0c451279e 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketServer.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WebsocketServer.java @@ -10,7 +10,6 @@ public class WebsocketServer extends AbstractWebsocketServer { private final ControllerApiWebsocketImpl parent; private final OnOpen onOpen; - private final OnRequest onRequest; private final OnNotification onNotification; private final OnError onError; private final OnClose onClose; @@ -20,7 +19,6 @@ public WebsocketServer(ControllerApiWebsocketImpl parent, String name, int port, super(name, port, poolSize, debugMode); this.parent = parent; this.onOpen = new OnOpen(parent); - this.onRequest = new OnRequest(parent); this.onNotification = new OnNotification(parent); this.onError = new OnError(parent); this.onClose = new OnClose(parent); @@ -38,7 +36,7 @@ protected OnOpen getOnOpen() { @Override protected OnRequest getOnRequest() { - return this.onRequest; + return this.parent.onRequest; } @Override diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WsData.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WsData.java index ed2f355872f..6a455a86972 100644 --- a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WsData.java +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/WsData.java @@ -13,7 +13,6 @@ import com.google.gson.JsonElement; import com.google.gson.JsonNull; -import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.notification.CurrentDataNotification; @@ -142,21 +141,6 @@ public Optional getUser() { return this.user; } - /** - * Throws an exception if the User is not authenticated. - * - * @param resource a resource identifier; used for the exception - * @return the current {@link User} - * @throws OpenemsNamedException if the current Role privileges are less - */ - public User assertUserIsAuthenticated(String resource) throws OpenemsNamedException { - if (this.getUser().isPresent()) { - return this.getUser().get(); - } - throw OpenemsError.COMMON_USER_NOT_AUTHENTICATED - .exception("Session [" + this.getSessionToken() + "]. Ignoring [" + resource + "]"); - } - @Override public String toString() { String tokenString; diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/AuthenticationRequestHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/AuthenticationRequestHandler.java new file mode 100644 index 00000000000..e36b38e030d --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/AuthenticationRequestHandler.java @@ -0,0 +1,103 @@ +package io.openems.edge.controller.api.websocket.handler; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsError; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.request.AuthenticateWithPasswordRequest; +import io.openems.common.jsonrpc.request.AuthenticateWithTokenRequest; +import io.openems.common.jsonrpc.request.LogoutRequest; +import io.openems.common.jsonrpc.response.AuthenticateResponse; +import io.openems.common.session.Language; +import io.openems.common.session.Role; +import io.openems.edge.common.jsonapi.EdgeGuards; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.user.User; +import io.openems.edge.common.user.UserService; +import io.openems.edge.controller.api.websocket.OnRequest; +import io.openems.edge.controller.api.websocket.Utils; +import io.openems.edge.controller.api.websocket.WsData; + +@Component(property = "entry=" + RootRequestHandler.ENTRY_POINT) +public class AuthenticationRequestHandler implements JsonApi { + + private final Logger log = LoggerFactory.getLogger(AuthenticationRequestHandler.class); + + private final Map sessionTokens = new ConcurrentHashMap<>(); + + @Reference + private UserService userService; + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(AuthenticateWithTokenRequest.METHOD, call -> { + final var request = AuthenticateWithTokenRequest.from(call.getRequest()); + var token = request.getToken(); + + return this.handleAuthentication(call.get(OnRequest.WS_DATA_KEY), request.getId(), + Optional.ofNullable(this.sessionTokens.get(token)), token); + }); + + builder.handleRequest(AuthenticateWithPasswordRequest.METHOD, call -> { + final var request = AuthenticateWithPasswordRequest.from(call.getRequest()); + + return this.handleAuthentication(call.get(OnRequest.WS_DATA_KEY), request.getId(), + this.userService.authenticate(request.password), UUID.randomUUID().toString()); + }); + + builder.handleRequest(LogoutRequest.METHOD, endpoint -> { + endpoint.setGuards(EdgeGuards.roleIsAtleast(Role.GUEST)); + }, call -> { + final var wsData = call.get(OnRequest.WS_DATA_KEY); + this.sessionTokens.remove(wsData.getSessionToken(), call.get(EdgeKeys.USER_KEY)); + wsData.logout(); + return new GenericJsonrpcResponseSuccess(call.getRequest().getId()); + }); + } + + /** + * Common handler for {@link AuthenticateWithTokenRequest} and + * {@link AuthenticateWithPasswordRequest}. + * + * @param wsData the WebSocket attachment + * @param requestId the ID of the original {@link JsonrpcRequest} + * @param userOpt the optional {@link User} + * @param token the existing or new token + * @return the JSON-RPC Success Response Future + * @throws OpenemsNamedException on error + */ + private JsonrpcResponseSuccess handleAuthentication(// + WsData wsData, // + UUID requestId, // + Optional userOpt, // + String token // + ) throws OpenemsNamedException { + if (userOpt.isEmpty()) { + wsData.unsetUser(); + throw OpenemsError.COMMON_AUTHENTICATION_FAILED.exception(); + } + final var user = userOpt.get(); + wsData.setSessionToken(token); + wsData.setUser(user); + this.sessionTokens.put(token, user); + this.log.info("User [" + user.getId() + ":" + user.getName() + "] connected."); + + return new AuthenticateResponse(requestId, token, user, List.of(Utils.getEdgeMetadata(user.getRole())), + Language.DEFAULT); + } + +} diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingComponentConfigRequestHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingComponentConfigRequestHandler.java new file mode 100644 index 00000000000..95184e286ab --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingComponentConfigRequestHandler.java @@ -0,0 +1,34 @@ +package io.openems.edge.controller.api.websocket.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.ComponentConfigRequestHandler; + +/** + * This class makes it possible to request component update, delete, ... + * {@link ComponentConfigRequestHandler} in a websocket connection. It just + * "binds" the component which handles the request and provides their methods + * indirectly. + */ +@Component(property = "entry=" + EdgeRpcRequestHandler.ENTRY_POINT) +public class BindingComponentConfigRequestHandler implements JsonApi { + + private final ComponentConfigRequestHandler componentConfigRequestHandler; + + @Activate + public BindingComponentConfigRequestHandler(// + @Reference ComponentConfigRequestHandler componentConfigRequestHandler // + ) { + this.componentConfigRequestHandler = componentConfigRequestHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.componentConfigRequestHandler.buildJsonApiRoutes(builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingComponentRequestHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingComponentRequestHandler.java new file mode 100644 index 00000000000..993629f3b93 --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingComponentRequestHandler.java @@ -0,0 +1,34 @@ +package io.openems.edge.controller.api.websocket.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import io.openems.common.jsonrpc.request.ComponentJsonApiRequest; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.ComponentRequestHandler; + +/** + * This class makes it possible to request {@link ComponentJsonApiRequest} in a + * websocket connection. It just "binds" the component which handles the request + * and provides their methods indirectly. + */ +@Component(property = "entry=" + EdgeRpcRequestHandler.ENTRY_POINT) +public class BindingComponentRequestHandler implements JsonApi { + + private final ComponentRequestHandler componentRequestHandler; + + @Activate + public BindingComponentRequestHandler(// + @Reference ComponentRequestHandler componentRequestHandler // + ) { + this.componentRequestHandler = componentRequestHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.componentRequestHandler.buildJsonApiRoutes(builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingQueryRequestHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingQueryRequestHandler.java new file mode 100644 index 00000000000..7b5ef449e45 --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingQueryRequestHandler.java @@ -0,0 +1,26 @@ +package io.openems.edge.controller.api.websocket.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.QueryRequestHandler; + +@Component(property = "entry=" + EdgeRpcRequestHandler.ENTRY_POINT) +public class BindingQueryRequestHandler implements JsonApi { + + private final QueryRequestHandler queryRequestHandler; + + @Activate + public BindingQueryRequestHandler(@Reference QueryRequestHandler handler) { + this.queryRequestHandler = handler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.queryRequestHandler.buildJsonApiRoutes(builder); + } + +} diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingRoutesJsonApiHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingRoutesJsonApiHandler.java new file mode 100644 index 00000000000..93bcc016179 --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/BindingRoutesJsonApiHandler.java @@ -0,0 +1,42 @@ +package io.openems.edge.controller.api.websocket.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceScope; +import org.osgi.service.component.annotations.ServiceScope; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.common.handler.RoutesJsonApiHandler; + +@Component(// + property = "entry=" + EdgeRpcRequestHandler.ENTRY_POINT, // + service = { BindingRoutesJsonApiHandler.class, JsonApi.class }, // + scope = ServiceScope.SINGLETON // +) +public class BindingRoutesJsonApiHandler implements JsonApi { + + private final RoutesJsonApiHandler jsonApiHandler; + + @Activate + public BindingRoutesJsonApiHandler(// + @Reference(scope = ReferenceScope.PROTOTYPE_REQUIRED) RoutesJsonApiHandler jsonApiHandler // + ) { + this.jsonApiHandler = jsonApiHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.jsonApiHandler.buildJsonApiRoutes(builder); + } + + public JsonApiBuilder getBuilder() { + return this.jsonApiHandler.getBuilder(); + } + + public void setBuilder(JsonApiBuilder builder) { + this.jsonApiHandler.setBuilder(builder); + } + +} \ No newline at end of file diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/EdgeRequestHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/EdgeRequestHandler.java new file mode 100644 index 00000000000..45234bfaa4f --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/EdgeRequestHandler.java @@ -0,0 +1,37 @@ +package io.openems.edge.controller.api.websocket.handler; + +import java.util.List; + +import org.osgi.service.component.annotations.Component; + +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.request.GetEdgeRequest; +import io.openems.common.jsonrpc.request.GetEdgesRequest; +import io.openems.common.jsonrpc.request.SubscribeEdgesRequest; +import io.openems.common.jsonrpc.response.GetEdgeResponse; +import io.openems.common.jsonrpc.response.GetEdgesResponse; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.websocket.Utils; + +@Component(property = "entry=" + RootRequestHandler.ENTRY_POINT) +public class EdgeRequestHandler implements JsonApi { + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(GetEdgesRequest.METHOD, call -> { + final var user = call.get(EdgeKeys.USER_KEY); + return new GetEdgesResponse(call.getRequest().getId(), + List.of(Utils.getEdgeMetadata(user.getGlobalRole()))); + }); + + builder.handleRequest(GetEdgeRequest.METHOD, call -> { + final var user = call.get(EdgeKeys.USER_KEY); + return new GetEdgeResponse(call.getRequest().getId(), Utils.getEdgeMetadata(user.getGlobalRole())); + }); + + builder.handleRequest(SubscribeEdgesRequest.METHOD, call -> new GenericJsonrpcResponseSuccess(call.getRequest().getId())); + } + +} diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/EdgeRpcRequestHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/EdgeRpcRequestHandler.java new file mode 100644 index 00000000000..8aa5e780c95 --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/EdgeRpcRequestHandler.java @@ -0,0 +1,65 @@ +package io.openems.edge.controller.api.websocket.handler; + +import java.util.List; + +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; + +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.request.EdgeRpcRequest; +import io.openems.common.jsonrpc.response.EdgeRpcResponse; +import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.jsonapi.MultipleJsonApiBinder; +import io.openems.edge.common.jsonapi.Subrequest; +import io.openems.edge.controller.api.websocket.ControllerApiWebsocket; + +@Component(property = "entry=" + RootRequestHandler.ENTRY_POINT) +public class EdgeRpcRequestHandler implements JsonApi { + + public static final String ENTRY_POINT = "edge.websocket.edgeRpc"; + + private final MultipleJsonApiBinder binder = new MultipleJsonApiBinder(); + + @Reference(// + cardinality = ReferenceCardinality.MULTIPLE, // + policy = ReferencePolicy.DYNAMIC, // + policyOption = ReferencePolicyOption.GREEDY, // + target = "(entry=" + ENTRY_POINT + ")" // + ) + protected void bindJsonApi(JsonApi jsonApi) { + this.binder.bindJsonApi(jsonApi); + } + + protected void unbindJsonApi(JsonApi jsonApi) { + this.binder.unbindJsonApi(jsonApi); + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.delegate(EdgeRpcRequest.METHOD, endpoint -> { + + }, call -> { + return EdgeRpcRequest.from(call.getRequest()).getPayload(); + }, b -> { + return this.binder.getJsonApiBuilder(); + }, response -> { + // wrap response in a EdgeRpcResponse if successful + if (response instanceof JsonrpcResponseSuccess success) { + return new EdgeRpcResponse(response.getId(), success); + } + return response; + }, () -> { + final var subrequest = new Subrequest(JsonUtils.buildJsonObject() // + .addProperty("edgeId", ControllerApiWebsocket.EDGE_ID) // + .build()); + subrequest.addRpcBuilderFor(this.binder.getJsonApiBuilder(), "payload"); + return List.of(subrequest); + }); + } + +} diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/RootRequestHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/RootRequestHandler.java new file mode 100644 index 00000000000..1080884c136 --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/RootRequestHandler.java @@ -0,0 +1,48 @@ +package io.openems.edge.controller.api.websocket.handler; + +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; + +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.jsonapi.MultipleJsonApiBinder; + +@Component(service = { RootRequestHandler.class, JsonApi.class }) +public class RootRequestHandler implements JsonApi { + + public static final String ENTRY_POINT = "edge.websocket.root"; + + private final MultipleJsonApiBinder apiBinder = new MultipleJsonApiBinder(); + private final BindingRoutesJsonApiHandler routesJsonApiHandler; + + @Reference(// + cardinality = ReferenceCardinality.MULTIPLE, // + policy = ReferencePolicy.DYNAMIC, // + policyOption = ReferencePolicyOption.GREEDY, // + target = "(entry=" + ENTRY_POINT + ")" // + ) + protected void bindJsonApi(JsonApi jsonApi) { + this.apiBinder.bindJsonApi(jsonApi); + this.routesJsonApiHandler.setBuilder(this.apiBinder.getJsonApiBuilder()); + } + + protected void unbindJsonApi(JsonApi jsonApi) { + this.apiBinder.unbindJsonApi(jsonApi); + this.routesJsonApiHandler.setBuilder(null); + } + + @Activate + public RootRequestHandler(@Reference BindingRoutesJsonApiHandler routesJsonApiHandler) { + this.routesJsonApiHandler = routesJsonApiHandler; + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.addBuilder(this.apiBinder.getJsonApiBuilder()); + } + +} diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/SubscribeChannelsRequestHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/SubscribeChannelsRequestHandler.java new file mode 100644 index 00000000000..dbf4e4c0a6d --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/SubscribeChannelsRequestHandler.java @@ -0,0 +1,24 @@ +package io.openems.edge.controller.api.websocket.handler; + +import org.osgi.service.component.annotations.Component; + +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.request.SubscribeChannelsRequest; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.websocket.OnRequest; + +@Component(property = "entry=" + EdgeRpcRequestHandler.ENTRY_POINT) +public class SubscribeChannelsRequestHandler implements JsonApi { + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(SubscribeChannelsRequest.METHOD, call -> { + final var request = SubscribeChannelsRequest.from(call.getRequest()); + call.get(OnRequest.WS_DATA_KEY).handleSubscribeChannelsRequest(request); + + return new GenericJsonrpcResponseSuccess(request.getId()); + }); + } + +} diff --git a/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/SubscribeSystemLogRequestHandler.java b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/SubscribeSystemLogRequestHandler.java new file mode 100644 index 00000000000..172251df193 --- /dev/null +++ b/io.openems.edge.controller.api.websocket/src/io/openems/edge/controller/api/websocket/handler/SubscribeSystemLogRequestHandler.java @@ -0,0 +1,75 @@ +package io.openems.edge.controller.api.websocket.handler; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.ops4j.pax.logging.spi.PaxAppender; +import org.ops4j.pax.logging.spi.PaxLoggingEvent; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; +import io.openems.common.jsonrpc.notification.EdgeRpcNotification; +import io.openems.common.jsonrpc.notification.SystemLogNotification; +import io.openems.common.jsonrpc.request.SubscribeSystemLogRequest; +import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.websocket.ControllerApiWebsocket; +import io.openems.edge.controller.api.websocket.OnRequest; +import io.openems.edge.controller.api.websocket.WsData; + +@Component(property = { // + "entry=" + EdgeRpcRequestHandler.ENTRY_POINT, // + "org.ops4j.pax.logging.appender.name=Controller.Api.Websocket" // +}) +public class SubscribeSystemLogRequestHandler implements JsonApi, PaxAppender { + + private final Logger log = LoggerFactory.getLogger(SubscribeSystemLogRequestHandler.class); + + private final Set subscribers = ConcurrentHashMap.newKeySet(); + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(SubscribeSystemLogRequest.METHOD, call -> { + final var request = SubscribeSystemLogRequest.from(call.getRequest()); + final var wsData = call.get(OnRequest.WS_DATA_KEY); + + if (request.isSubscribe()) { + this.subscribers.add(wsData); + } else { + this.subscribers.remove(wsData); + } + + return new GenericJsonrpcResponseSuccess(request.getId()); + }); + } + + @Override + public void doAppend(PaxLoggingEvent event) { + if (this.subscribers.isEmpty()) { + return; + } + + final var notification = new EdgeRpcNotification(ControllerApiWebsocket.EDGE_ID, + SystemLogNotification.fromPaxLoggingEvent(event)); + + final var iter = this.subscribers.iterator(); + while (iter.hasNext()) { + final var wsData = iter.next(); + + if (wsData.getWebsocket().isFlushAndClose()) { + iter.remove(); + continue; + } + try { + wsData.send(notification); + } catch (OpenemsException e) { + this.log.warn("Unable to handle PaxLoggingEvent", e); + iter.remove(); + } + } + } + +} diff --git a/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/ControllerApiWebsocketImplTest.java b/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/ControllerApiWebsocketImplTest.java index 058fa42d5f4..b37f1fb4929 100644 --- a/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/ControllerApiWebsocketImplTest.java +++ b/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/ControllerApiWebsocketImplTest.java @@ -14,6 +14,7 @@ public class ControllerApiWebsocketImplTest { public void test() throws Exception { new ControllerTest(new ControllerApiWebsocketImpl()) // .addReference("componentManager", new DummyComponentManager()) // + .addReference("onRequestFactory", new DummyOnRequestFactory()) // .activate(MyConfig.create() // .setId(CTRL_ID) // .setApiTimeout(60) // diff --git a/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/DummyOnRequestFactory.java b/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/DummyOnRequestFactory.java new file mode 100644 index 00000000000..a953b39803c --- /dev/null +++ b/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/DummyOnRequestFactory.java @@ -0,0 +1,36 @@ +package io.openems.edge.controller.api.websocket; + +import java.lang.reflect.InvocationTargetException; + +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentServiceObjects; + +import io.openems.common.utils.ReflectionUtils; + +public class DummyOnRequestFactory extends OnRequest.Factory { + + public DummyOnRequestFactory() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + super(); + ReflectionUtils.setAttribute(OnRequest.Factory.class, this, "cso", new DummyOnRequestCso()); + } + + private static class DummyOnRequestCso implements ComponentServiceObjects { + + @Override + public OnRequest getService() { + return new OnRequest(); + } + + @Override + public void ungetService(OnRequest service) { + // empty for tests + } + + @Override + public ServiceReference getServiceReference() { + // empty for tests + return null; + } + } + +} diff --git a/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/MyConfig.java b/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/MyConfig.java index d3a294ee563..f234dea0c9d 100644 --- a/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/MyConfig.java +++ b/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/MyConfig.java @@ -9,6 +9,7 @@ protected static class Builder { private String id; private int port; private int apiTimeout; + private boolean debugMode; private Builder() { } @@ -28,6 +29,11 @@ public Builder setPort(int port) { return this; } + public Builder setDebugMode(boolean debugMode) { + this.debugMode = debugMode; + return this; + } + public MyConfig build() { return new MyConfig(this); } @@ -58,4 +64,10 @@ public int port() { public int apiTimeout() { return this.builder.apiTimeout; } + + @Override + public boolean debugMode() { + return this.builder.debugMode; + } + } \ No newline at end of file diff --git a/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/OnRequestTest.java b/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/OnRequestTest.java index 514e7e363f3..34295fb43d4 100644 --- a/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/OnRequestTest.java +++ b/io.openems.edge.controller.api.websocket/test/io/openems/edge/controller/api/websocket/OnRequestTest.java @@ -5,24 +5,45 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import org.junit.Before; import org.junit.Test; import io.openems.common.OpenemsConstants; import io.openems.common.jsonrpc.base.GenericJsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcRequest; +import io.openems.common.jsonrpc.base.JsonrpcResponse; import io.openems.common.jsonrpc.request.GetEdgeRequest; import io.openems.common.jsonrpc.request.GetEdgesRequest; +import io.openems.common.jsonrpc.response.GetEdgeResponse; +import io.openems.common.jsonrpc.response.GetEdgesResponse; import io.openems.common.jsonrpc.response.GetEdgesResponse.EdgeMetadata; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.Call; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.controller.api.websocket.handler.EdgeRequestHandler; public class OnRequestTest { + private JsonApiBuilder api; + + @Before + public void before() { + this.api = new JsonApiBuilder(); + new EdgeRequestHandler().buildJsonApiRoutes(this.api); + } + @Test public void testHandleGetEdgesRequest() throws Exception { - final var response = OnRequest.handleGetEdgesRequest(DUMMY_ADMIN, + final var call = new Call( GetEdgesRequest.from(new GenericJsonrpcRequest(GetEdgesRequest.METHOD, JsonUtils.buildJsonObject() // .addProperty("page", 0) // - .build()))) - .get(); + .build()))); + call.put(EdgeKeys.USER_KEY, DUMMY_ADMIN); + + this.api.handle(call); + + final var response = (GetEdgesResponse) call.getResponse(); final var edges = response.edgeMetadata; assertEquals(1, edges.size()); @@ -32,11 +53,15 @@ public void testHandleGetEdgesRequest() throws Exception { @Test public void testHandleGetEdgeRequest() throws Exception { - final var response = OnRequest.handleGetEdgeRequest(DUMMY_ADMIN, + final var call = new Call( GetEdgeRequest.from(new GenericJsonrpcRequest(GetEdgeRequest.METHOD, JsonUtils.buildJsonObject() // .addProperty("edgeId", ControllerApiWebsocket.EDGE_ID) // - .build()))) - .get(); + .build()))); + call.put(EdgeKeys.USER_KEY, DUMMY_ADMIN); + + this.api.handle(call); + + final var response = (GetEdgeResponse) call.getResponse(); this.validateLocalEdgeMetadata(response.edgeMetadata); } diff --git a/io.openems.edge.controller.ess.timeofusetariff/bnd.bnd b/io.openems.edge.controller.ess.timeofusetariff/bnd.bnd index 8f894150414..d45bd8553ce 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/bnd.bnd +++ b/io.openems.edge.controller.ess.timeofusetariff/bnd.bnd @@ -10,11 +10,10 @@ Bundle-Version: 1.0.0.${tstamp} io.openems.edge.controller.api,\ io.openems.edge.controller.ess.emergencycapacityreserve,\ io.openems.edge.controller.ess.limittotaldischarge,\ + io.openems.edge.energy.api,\ io.openems.edge.ess.api,\ - io.openems.edge.predictor.api,\ io.openems.edge.timedata.api,\ io.openems.edge.timeofusetariff.api,\ - io.openems.wrapper.jenetics,\ -testpath: \ ${testpath},\ diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffController.java b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffController.java index 7950bdbb387..9876f79201e 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffController.java +++ b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffController.java @@ -22,7 +22,7 @@ public enum ChannelId implements io.openems.edge.common.channel.ChannelId { .text("Current state of the Controller")), QUARTERLY_PRICES(Doc.of(OpenemsType.DOUBLE) // - .unit(Unit.EUROS_PER_MEGAWATT_HOUR) // + .unit(Unit.MONEY_PER_MEGAWATT_HOUR) // .text("Price of the electricity for the current Hour")// .persistencePriority(PersistencePriority.HIGH)), // diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffControllerImpl.java b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffControllerImpl.java index 2646728095b..5ae75684899 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffControllerImpl.java +++ b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffControllerImpl.java @@ -3,14 +3,9 @@ import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.calculateChargeGridPower; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.calculateDelayDischargePower; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.getEssMinSocPercentage; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.postprocessRunState; +import static io.openems.edge.controller.ess.timeofusetariff.Utils.calculateAutomaticMode; -import java.time.ZonedDateTime; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import org.osgi.service.cm.ConfigurationAdmin; @@ -26,28 +21,24 @@ import org.osgi.service.component.annotations.ReferencePolicyOption; import org.osgi.service.metatype.annotations.Designate; -import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; -import io.openems.common.session.Role; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; import io.openems.edge.common.sum.Sum; -import io.openems.edge.common.user.User; import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.ess.emergencycapacityreserve.ControllerEssEmergencyCapacityReserve; import io.openems.edge.controller.ess.limittotaldischarge.ControllerEssLimitTotalDischarge; -import io.openems.edge.controller.ess.timeofusetariff.jsonrpc.GetScheduleRequest; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Context; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Optimizer; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils; +import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImpl.Context; +import io.openems.edge.controller.ess.timeofusetariff.Utils.ApplyState; +import io.openems.edge.energy.api.EnergySchedulable; +import io.openems.edge.energy.api.EnergyScheduleHandler; +import io.openems.edge.energy.api.EnergyScheduler; import io.openems.edge.ess.api.ManagedSymmetricEss; import io.openems.edge.ess.power.api.Phase; import io.openems.edge.ess.power.api.Pwr; -import io.openems.edge.predictor.api.manager.PredictorManager; import io.openems.edge.timedata.api.Timedata; import io.openems.edge.timedata.api.TimedataProvider; import io.openems.edge.timedata.api.utils.CalculateActiveTime; @@ -59,12 +50,15 @@ immediate = true, // configurationPolicy = ConfigurationPolicy.REQUIRE // ) -public class TimeOfUseTariffControllerImpl extends AbstractOpenemsComponent - implements TimeOfUseTariffController, Controller, OpenemsComponent, TimedataProvider, JsonApi { +public class TimeOfUseTariffControllerImpl extends AbstractOpenemsComponent implements TimeOfUseTariffController, + EnergySchedulable, Controller, OpenemsComponent, TimedataProvider, ComponentJsonApi { - /** The hard working Worker. */ - private final Optimizer optimizer; + public static record Context(List ctrlEmergencyCapacityReserves, + List ctrlLimitTotalDischarges, ManagedSymmetricEss ess, + ControlMode controlMode, int maxChargePowerFromGrid, boolean limitChargePowerFor14aEnWG) { + } + private final EnergyScheduleHandler energyScheduleHandler; private final CalculateActiveTime calculateDelayedTime = new CalculateActiveTime(this, TimeOfUseTariffController.ChannelId.DELAYED_TIME); private final CalculateActiveTime calculateChargedTime = new CalculateActiveTime(this, @@ -77,14 +71,12 @@ public class TimeOfUseTariffControllerImpl extends AbstractOpenemsComponent private ComponentManager componentManager; @Reference - private PredictorManager predictorManager; + private Sum sum; + // This is only required to get the current price for UI chart @Reference private TimeOfUseTariff timeOfUseTariff; - @Reference - private Sum sum; - @Reference(policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) private volatile Timedata timedata; @@ -101,6 +93,9 @@ public class TimeOfUseTariffControllerImpl extends AbstractOpenemsComponent @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) private ManagedSymmetricEss ess; + @Reference + private EnergyScheduler energyScheduler; + private Config config = null; public TimeOfUseTariffControllerImpl() { @@ -109,189 +104,91 @@ public TimeOfUseTariffControllerImpl() { Controller.ChannelId.values(), // TimeOfUseTariffController.ChannelId.values() // ); - - // Prepare Optimizer and Context - this.optimizer = new Optimizer(() -> Context.create() // - .setClock(this.componentManager.getClock()) // - .setSum(this.sum) // - .setPredictorManager(this.predictorManager) // - .setTimeOfUseTariff(this.timeOfUseTariff) // - .setEss(this.ess) // - .setCtrlEmergencyCapacityReserves(this.ctrlEmergencyCapacityReserves) // - .setCtrlLimitTotalDischarges(this.ctrlLimitTotalDischarges) // - .setControlMode(this.config.controlMode()) // - .setMaxChargePowerFromGrid(this.config.maxChargePowerFromGrid()) // - .setLimitChargePowerFor14aEnWG(this.config.limitChargePowerFor14aEnWG()) // - .build()); + this.energyScheduleHandler = new EnergyScheduleHandler<>(// + () -> this.config.controlMode().states, // + () -> new Context(this.ctrlEmergencyCapacityReserves, this.ctrlLimitTotalDischarges, this.ess, + this.config.controlMode(), this.config.maxChargePowerFromGrid(), + this.config.limitChargePowerFor14aEnWG())); } @Activate private void activate(ComponentContext context, Config config) { super.activate(context, config.id(), config.alias(), config.enabled()); - if (this.applyConfig(config)) { - this.optimizer.activate(this.id()); - } + this.applyConfig(config); } @Modified private void modified(ComponentContext context, Config config) { super.modified(context, config.id(), config.alias(), config.enabled()); - if (this.applyConfig(config)) { - this.optimizer.modified(this.id()); - } + this.applyConfig(config); } - private synchronized boolean applyConfig(Config config) { + private synchronized void applyConfig(Config config) { this.config = config; // update filter for 'ess' - if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "ess", config.ess_id())) { - return false; - } - - if (!config.enabled()) { - this.optimizer.deactivate(); - return false; - } - - return true; + OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "ess", config.ess_id()); } @Override @Deactivate protected void deactivate() { - this.optimizer.deactivate(); super.deactivate(); } @Override public void run() throws OpenemsNamedException { // Mode given from the configuration. - switch (this.config.mode()) { - case AUTOMATIC -> this.modeAutomatic(); - case OFF -> this.modeOff(); - } + var as = switch (this.config.mode()) { + case AUTOMATIC -> calculateAutomaticMode(this.sum, this.ess, + this.energyScheduleHandler.getCurrentEssChargeInChargeGrid(), this.config.maxChargePowerFromGrid(), + this.config.limitChargePowerFor14aEnWG(), this.getCurrentPeriodState()); + case OFF -> new ApplyState(StateMachine.BALANCING, null); + }; - this.updateVisualizationChannels(); - } + // Update Channels + this._setStateMachine(as.actualState()); + this.calculateChargedTime.update(as.actualState() == CHARGE_GRID); + this.calculateDelayedTime.update(as.actualState() == DELAY_DISCHARGE); + this._setQuarterlyPrices(this.timeOfUseTariff.getPrices().getFirst()); - private StateMachine getCurrentStateMachine() { - return this.optimizer.getCurrentStateMachine(); + // Apply ActivePower set-point + if (as.setPoint() != null) { + this.ess.setActivePowerLessOrEquals(this.ess.getPower().fitValueIntoMinMaxPower(this.id(), this.ess, + Phase.ALL, Pwr.ACTIVE, as.setPoint())); + } } private StateMachine getCurrentPeriodState() { - var state = this.getCurrentStateMachine(); + var state = this.energyScheduleHandler.getCurrentState(); if (state != null) { return state; } return BALANCING; // Default Fallback } - /** - * Apply the Schedule. - * - * @throws OpenemsNamedException on error - */ - private void modeAutomatic() throws OpenemsNamedException { - // Evaluate current state - final var state = postprocessRunState( - getEssMinSocPercentage(this.ctrlLimitTotalDischarges, this.ctrlEmergencyCapacityReserves), // - this.ess.getSoc().get(), // - this.sum.getProductionActivePower().orElse(0), // - this.getCurrentPeriodState()); - this._setStateMachine(state); - - // Update the timer. - this.calculateChargedTime.update(state == CHARGE_GRID); - this.calculateDelayedTime.update(state == DELAY_DISCHARGE); - - // Get and apply ActivePower Less-or-Equals Set-Point - var activePower = switch (state) { - case CHARGE_GRID -> calculateChargeGridPower(this.optimizer.getParams(), this.ess, this.sum, - this.config.maxChargePowerFromGrid(), this.config.limitChargePowerFor14aEnWG()); - case DELAY_DISCHARGE -> calculateDelayDischargePower(this.ess); - case BALANCING -> null; - }; - - if (activePower != null) { - this.ess.setActivePowerLessOrEquals(this.ess.getPower().fitValueIntoMinMaxPower(this.id(), this.ess, - Phase.ALL, Pwr.ACTIVE, activePower)); - } - } - - /** - * Apply the mode OFF logic. Sets the Default values to the channels, if the - * Mode is 'OFF'. - */ - private void modeOff() { - // Update the timer. - this.calculateChargedTime.update(false); - this.calculateDelayedTime.update(false); - - // Default State Machine. - this._setStateMachine(BALANCING); - } - - /** - * This is only to visualize data for better debugging. - */ - private void updateVisualizationChannels() { - final Double quarterlyPrice; - var period = this.getCurrentStateMachine(); - if (period == null) { - // Values are not available. - quarterlyPrice = this.timeOfUseTariff.getPrices().getFirst(); - - } else { - var params = this.optimizer.getParams(); - if (params != null) { - // First period is always the current period. - quarterlyPrice = params.optimizePeriods().get(0).price(); - } else { - quarterlyPrice = null; - } - } - - // Set the channels - this._setQuarterlyPrices(quarterlyPrice); - } - @Override public Timedata getTimedata() { return this.timedata; } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException { - user.assertRoleIsAtLeast("handleJsonrpcRequest", Role.GUEST); - return switch (request.getMethod()) { - case GetScheduleRequest.METHOD -> this.handleGetScheduleRequest(user, GetScheduleRequest.from(request)); - default -> throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - }; - } - - /** - * Handles a {@link GetScheduleRequest}. - * - * @param user the User - * @param request the GetScheduleRequest - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleGetScheduleRequest(User user, - GetScheduleRequest request) throws OpenemsNamedException { - return CompletableFuture.completedFuture(Utils.handleGetScheduleRequest(this.optimizer, request.getId(), - this.timedata, this.id(), ZonedDateTime.now(this.componentManager.getClock()))); + public void buildJsonApiRoutes(JsonApiBuilder builder) { + this.energyScheduler.buildJsonApiRoutes(builder); } @Override public String debugLog() { var b = new StringBuilder() // .append(this.getStateMachine()); // - if (this.getCurrentStateMachine() == null) { + if (this.getCurrentPeriodState() == null) { b.append("|No Schedule available"); } return b.toString(); } + + @Override + public EnergyScheduleHandler getEnergyScheduleHandler() { + return this.energyScheduleHandler; + } } diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/Utils.java b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/Utils.java new file mode 100644 index 00000000000..6543f9b24c1 --- /dev/null +++ b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/Utils.java @@ -0,0 +1,247 @@ +package io.openems.edge.controller.ess.timeofusetariff; + +import static io.openems.edge.common.type.TypeUtils.multiply; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; +import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffController.PERIODS_PER_HOUR; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.lang.Math.round; +import static java.util.stream.IntStream.concat; + +import java.util.List; +import java.util.Objects; + +import io.openems.common.types.ChannelAddress; +import io.openems.edge.common.sum.Sum; +import io.openems.edge.controller.api.Controller; +import io.openems.edge.controller.ess.emergencycapacityreserve.ControllerEssEmergencyCapacityReserve; +import io.openems.edge.controller.ess.limittotaldischarge.ControllerEssLimitTotalDischarge; +import io.openems.edge.ess.api.HybridEss; +import io.openems.edge.ess.api.ManagedSymmetricEss; + +/** + * Utils for {@link TimeOfUseTariffController}. + * + *

+ * All energy values are in [Wh] and positive, unless stated differently. + */ +public final class Utils { + + private Utils() { + } + + /** Keep some buffer to avoid scheduling errors because of bad predictions. */ + public static final float ESS_MAX_SOC = 90F; + + /** Limit Charge Power for §14a EnWG. */ + public static final int ESS_LIMIT_14A_ENWG = -4200; + + /** + * C-Rate (capacity divided by time) during {@link StateMachine#CHARGE_GRID}. + * With a C-Rate of 0.5 the battery gets fully charged within 2 hours. + */ + public static final float ESS_CHARGE_C_RATE = 0.5F; + + public static final ChannelAddress SUM_PRODUCTION = new ChannelAddress("_sum", "ProductionActivePower"); + public static final ChannelAddress SUM_CONSUMPTION = new ChannelAddress("_sum", "ConsumptionActivePower"); + public static final ChannelAddress SUM_GRID = new ChannelAddress("_sum", "GridActivePower"); + public static final ChannelAddress SUM_UNMANAGED_CONSUMPTION = new ChannelAddress("_sum", + "UnmanagedConsumptionActivePower"); + public static final ChannelAddress SUM_ESS_DISCHARGE_POWER = new ChannelAddress("_sum", "EssDischargePower"); + public static final ChannelAddress SUM_ESS_SOC = new ChannelAddress("_sum", "EssSoc"); + + /** + * Returns the configured minimum SoC, or zero. + * + * @param ctrlLimitTotalDischarges the list of + * {@link ControllerEssLimitTotalDischarge} + * @param ctrlEmergencyCapacityReserves the list of + * {@link ControllerEssEmergencyCapacityReserve} + * @return the value in [%] + */ + public static int getEssMinSocPercentage(List ctrlLimitTotalDischarges, + List ctrlEmergencyCapacityReserves) { + return concat(// + ctrlLimitTotalDischarges.stream() // + .map(ctrl -> ctrl.getMinSoc().get()) // + .filter(Objects::nonNull) // + .mapToInt(v -> max(0, v)), // only positives + ctrlEmergencyCapacityReserves.stream() // + .map(ctrl -> ctrl.getActualReserveSoc().get()) // + .filter(Objects::nonNull) // + .mapToInt(v -> max(0, v))) // only positives + .max().orElse(0); + } + + public static record ApplyState(StateMachine actualState, Integer setPoint) { + } + + /** + * Calculate Automatic Mode. + * + * @param sum the {@link Sum} + * @param ess the {@link ManagedSymmetricEss} + * @param essChargeInChargeGrid ESS Charge Energy in CHARGE_GRID State [Wh] + * @param maxChargePowerFromGrid the configured max charge from grid power + * @param limitChargePowerFor14aEnWG Limit Charge Power for §14a EnWG + * @param targetState the scheduled target {@link StateMachine} + * @return {@link ApplyState} + */ + public static ApplyState calculateAutomaticMode(Sum sum, ManagedSymmetricEss ess, Integer essChargeInChargeGrid, + int maxChargePowerFromGrid, boolean limitChargePowerFor14aEnWG, StateMachine targetState) { + final StateMachine actualState; + final Integer setPoint; + + var gridActivePower = sum.getGridActivePower().get(); // current buy-from/sell-to grid + var essActivePower = ess.getActivePower().get(); // current charge/discharge ESS + if (gridActivePower == null || essActivePower == null) { + // undefined state + return new ApplyState(BALANCING, null); + } + + // Post-process and get actual state + final var pwrBalancing = gridActivePower + essActivePower; + final var pwrDelayDischarge = calculateDelayDischargePower(ess); + final var pwrChargeGrid = calculateChargeGridPower(essChargeInChargeGrid, ess, essActivePower, gridActivePower, + maxChargePowerFromGrid, limitChargePowerFor14aEnWG); + actualState = postprocessRunState(targetState, pwrBalancing, pwrDelayDischarge, pwrChargeGrid); + + // Get and apply ActivePower Less-or-Equals Set-Point + setPoint = switch (actualState) { + case BALANCING -> null; // delegate to next priority Controller + case DELAY_DISCHARGE -> pwrDelayDischarge; + case CHARGE_GRID -> pwrChargeGrid; + }; + + return new ApplyState(actualState, setPoint); + } + + /** + * Post-Process a state during {@link Controller#run()}, i.e. replace with + * 'better' state if appropriate. + * + *

+ * NOTE: this can be useful, if live operation deviates from predicted + * operation, e.g. because predictions were wrong. + * + * @param state the initial state + * @param pwrBalancing the power set-point as it would be in + * {@link StateMachine#BALANCING} + * @param pwrDelayDischarge the power set-point as it would be in + * {@link StateMachine#DELAY_DISCHARGE} + * @param pwrChargeGrid the power set-point as it would be in + * {@link StateMachine#CHARGE_GRID} + * @return the new state + */ + public static StateMachine postprocessRunState(StateMachine state, int pwrBalancing, int pwrDelayDischarge, + int pwrChargeGrid) { + if (state == CHARGE_GRID) { + // CHARGE_GRID,... + if (pwrChargeGrid >= pwrDelayDischarge) { + // but battery charge/discharge is the same as DELAY_DISCHARGE + state = DELAY_DISCHARGE; + } + } + + if (state == DELAY_DISCHARGE) { + // CHARGE_GRID,... + if (pwrDelayDischarge >= pwrBalancing) { + // but battery charge/discharge is the same as DELAY_DISCHARGE + state = BALANCING; + } + } + + return state; + } + + protected static int calculateEssChargeInChargeGridPowerFromParams(Integer essChargeInChargeGrid, + ManagedSymmetricEss ess) { + if (essChargeInChargeGrid != null) { + return toPower(essChargeInChargeGrid); + } + var capacity = ess.getCapacity(); + if (capacity.isDefined()) { + return round(capacity.get() * ESS_CHARGE_C_RATE); + } + var maxApparentPower = ess.getMaxApparentPower(); + if (maxApparentPower.isDefined()) { + return maxApparentPower.get() / 4; + } + return 0; + } + + /** + * Calculates the Max-ActivePower constraint for + * {@link StateMachine#CHARGE_GRID}. + * + * @param essChargeInChargeGrid ESS Charge Energy in CHARGE_GRID State [Wh] + * @param ess the {@link ManagedSymmetricEss} + * @param essActivePower the ESS ActivePower + * @param gridActivePower the Grid ActivePower + * @param maxChargePowerFromGrid the configured max charge from grid power + * @param limitChargePowerFor14aEnWG Limit Charge Power for §14a EnWG + * @return the set-point or null + */ + public static int calculateChargeGridPower(Integer essChargeInChargeGrid, ManagedSymmetricEss ess, + int essActivePower, int gridActivePower, int maxChargePowerFromGrid, boolean limitChargePowerFor14aEnWG) { + var realGridPower = gridActivePower + essActivePower; // 'real', without current ESS charge/discharge + var targetChargePower = calculateEssChargeInChargeGridPowerFromParams(essChargeInChargeGrid, ess) // + + min(0, realGridPower) * -1; // add excess production + var effectiveGridBuyPower = max(0, realGridPower) + targetChargePower; + var chargePower = max(0, targetChargePower - max(0, effectiveGridBuyPower - maxChargePowerFromGrid)); + + // Invert to negative for CHARGE + chargePower *= -1; + + // Apply §14a EnWG limit + if (limitChargePowerFor14aEnWG) { + chargePower = max(ESS_LIMIT_14A_ENWG, chargePower); + } + + return chargePower; + } + + /** + * Calculates the Max-ActivePower constraint for + * {@link StateMachine#CHARGE_PRODUCTION}. + * + * @param sum the {@link Sum} + * @return the set-point + */ + public static Integer calculateMaxChargeProductionPower(Sum sum) { + var productionAcActivePower = sum.getProductionAcActivePower().get(); + if (productionAcActivePower == null || productionAcActivePower < 0) { + return 0; // unknown AC production -> do not charge + } + return -productionAcActivePower; + } + + /** + * Calculates the ActivePower constraint for + * {@link StateMachine#DELAY_DISCHARGE}. + * + * @param ess the {@link ManagedSymmetricEss} + * @return the set-point + */ + public static int calculateDelayDischargePower(ManagedSymmetricEss ess) { + if (ess instanceof HybridEss e) { + // Limit discharge to DC-PV power + return max(0, ess.getActivePower().orElse(0) - e.getDcDischargePower().orElse(0)); + } else { + // Limit discharge to 0 + return 0; + } + } + + /** + * Converts energy [Wh/15 min] to power [W]. + * + * @param energy the energy value + * @return the power value + */ + private static Integer toPower(Integer energy) { + return multiply(energy, PERIODS_PER_HOUR); + } +} diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/GetScheduleResponse.java b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/GetScheduleResponse.java deleted file mode 100644 index 4464d3548d4..00000000000 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/GetScheduleResponse.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.openems.edge.controller.ess.timeofusetariff.jsonrpc; - -import java.time.ZonedDateTime; -import java.util.UUID; - -import com.google.gson.JsonObject; - -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; -import io.openems.common.utils.JsonUtils; - -/** - * Represents a JSON-RPC Response for 'getMeters'. - * - *

- * {
- *   "jsonrpc": "2.0",
- *   "id": "UUID",
- *   "result": {
- *     'schedule': [{
- *      'timestamp':...,
- *      'price':...,
- *      'state':...,
- *      'grid':...,
- *      'production':...,
- *      'consumption':...,
- *      'ess':...,
- *      'soc':...,
- *     }]
- *   }
- * }
- * 
- */ -public class GetScheduleResponse extends JsonrpcResponseSuccess { - - private final ZonedDateTime fromDate; - private final ScheduleDatas scheduleDatas; - - public GetScheduleResponse(ZonedDateTime fromDate, ScheduleDatas scheduleDatas) { - this(UUID.randomUUID(), fromDate, scheduleDatas); - } - - public GetScheduleResponse(UUID id, ZonedDateTime fromDate, ScheduleDatas scheduleDatas) { - super(id); - this.scheduleDatas = scheduleDatas; - this.fromDate = fromDate; - } - - @Override - public JsonObject getResult() { - return JsonUtils.buildJsonObject() // - .add("schedule", this.scheduleDatas.toJsonArray(this.fromDate)) // - .build(); - } - -} diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Context.java b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Context.java deleted file mode 100644 index d7d5dd8724c..00000000000 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Context.java +++ /dev/null @@ -1,172 +0,0 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; - -import java.time.Clock; -import java.util.List; - -import io.openems.edge.common.sum.Sum; -import io.openems.edge.controller.ess.emergencycapacityreserve.ControllerEssEmergencyCapacityReserve; -import io.openems.edge.controller.ess.limittotaldischarge.ControllerEssLimitTotalDischarge; -import io.openems.edge.controller.ess.timeofusetariff.ControlMode; -import io.openems.edge.ess.api.ManagedSymmetricEss; -import io.openems.edge.predictor.api.manager.PredictorManager; -import io.openems.edge.timeofusetariff.api.TimeOfUseTariff; - -public record Context(// - Clock clock, // - Sum sum, // - PredictorManager predictorManager, // - TimeOfUseTariff timeOfUseTariff, // - ManagedSymmetricEss ess, // - List ctrlEmergencyCapacityReserves, // - List ctrlLimitTotalDischarges, // - ControlMode controlMode, // - int maxChargePowerFromGrid, // - boolean limitChargePowerFor14aEnWG) { - - public static class Builder { - private Clock clock; - private Sum sum; - private PredictorManager predictorManager; - private TimeOfUseTariff timeOfUseTariff; - private ManagedSymmetricEss ess; - private List ctrlEmergencyCapacityReserves; - private List ctrlLimitTotalDischarges; - private ControlMode controlMode; - private int maxChargePowerFromGrid; - private boolean limitChargePowerFor14aEnWG; - - /** - * The {@link Clock}. - * - * @param clock the {@link Clock} - * @return myself - */ - public Builder setClock(Clock clock) { - this.clock = clock; - return this; - } - - /** - * The {@link Sum}. - * - * @param sum the {@link Sum} - * @return myself - */ - public Builder setSum(Sum sum) { - this.sum = sum; - return this; - } - - /** - * The {@link PredictorManager}. - * - * @param predictorManager the {@link PredictorManager} - * @return myself - */ - public Builder setPredictorManager(PredictorManager predictorManager) { - this.predictorManager = predictorManager; - return this; - } - - /** - * The {@link TimeOfUseTariff}. - * - * @param timeOfUseTariff the {@link TimeOfUseTariff} - * @return myself - */ - public Builder setTimeOfUseTariff(TimeOfUseTariff timeOfUseTariff) { - this.timeOfUseTariff = timeOfUseTariff; - return this; - } - - /** - * The {@link ManagedSymmetricEss}. - * - * @param ess the {@link ManagedSymmetricEss} - * @return myself - */ - public Builder setEss(ManagedSymmetricEss ess) { - this.ess = ess; - return this; - } - - /** - * The list of {@link ControllerEssEmergencyCapacityReserve}. - * - * @param ctrlEmergencyCapacityReserves the list of - * {@link ControllerEssEmergencyCapacityReserve} - * @return myself - */ - public Builder setCtrlEmergencyCapacityReserves( - List ctrlEmergencyCapacityReserves) { - this.ctrlEmergencyCapacityReserves = ctrlEmergencyCapacityReserves; - return this; - } - - /** - * The list of {@link ControllerEssLimitTotalDischarge}. - * - * @param ctrlLimitTotalDischarges the list of - * {@link ControllerEssLimitTotalDischarge} - * @return myself - */ - public Builder setCtrlLimitTotalDischarges(List ctrlLimitTotalDischarges) { - this.ctrlLimitTotalDischarges = ctrlLimitTotalDischarges; - return this; - } - - /** - * The {@link ControlMode}. - * - * @param controlMode the {@link ControlMode} - * @return myself - */ - public Builder setControlMode(ControlMode controlMode) { - this.controlMode = controlMode; - return this; - } - - /** - * The maxChargePowerFromGrid. - * - * @param maxChargePowerFromGrid the maxChargePowerFromGrid - * @return myself - */ - public Builder setMaxChargePowerFromGrid(int maxChargePowerFromGrid) { - this.maxChargePowerFromGrid = maxChargePowerFromGrid; - return this; - } - - /** - * Always apply 14a EnWG limit of 4.2 kW. - * - * @param limitChargePowerFor14aEnWG boolean - * @return myself - */ - public Builder setLimitChargePowerFor14aEnWG(boolean limitChargePowerFor14aEnWG) { - this.limitChargePowerFor14aEnWG = limitChargePowerFor14aEnWG; - return this; - } - - /** - * Builds the {@link Context}. - * - * @return the {@link Context} record - */ - public Context build() { - return new Context(this.clock, this.sum, this.predictorManager, this.timeOfUseTariff, this.ess, - this.ctrlEmergencyCapacityReserves, this.ctrlLimitTotalDischarges, this.controlMode, - this.maxChargePowerFromGrid, this.limitChargePowerFor14aEnWG); - } - } - - /** - * Create a {@link Context} {@link Builder}. - * - * @return a {@link Builder} - */ - public static Builder create() { - return new Context.Builder(); - } - -} diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/package-info.java b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/package-info.java new file mode 100644 index 00000000000..7b31142b53d --- /dev/null +++ b/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/package-info.java @@ -0,0 +1,3 @@ +@org.osgi.annotation.versioning.Version("1.0.0") +@org.osgi.annotation.bundle.Export +package io.openems.edge.controller.ess.timeofusetariff; diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffControllerImplTest.java b/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffControllerImplTest.java index f7d4af753f2..d3f32289678 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffControllerImplTest.java +++ b/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/TimeOfUseTariffControllerImplTest.java @@ -1,20 +1,12 @@ package io.openems.edge.controller.ess.timeofusetariff; -import static io.openems.common.utils.DateUtils.roundDownToQuarter; import static io.openems.edge.controller.ess.timeofusetariff.ControlMode.CHARGE_CONSUMPTION; import static io.openems.edge.controller.ess.timeofusetariff.Mode.AUTOMATIC; import static io.openems.edge.controller.ess.timeofusetariff.RiskLevel.MEDIUM; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.CONSUMPTION_PREDICTION_QUARTERLY; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PRODUCTION_PREDICTION_QUARTERLY; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_CONSUMPTION; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_PRODUCTION; -import static java.time.temporal.ChronoUnit.DAYS; import java.time.Clock; import java.time.Instant; import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.util.function.Supplier; import org.junit.Test; @@ -23,13 +15,8 @@ import io.openems.edge.common.test.AbstractComponentTest.TestCase; import io.openems.edge.common.test.DummyComponentManager; import io.openems.edge.common.test.DummyConfigurationAdmin; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Context; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Optimizer; import io.openems.edge.controller.test.ControllerTest; import io.openems.edge.ess.test.DummyManagedSymmetricEss; -import io.openems.edge.predictor.api.prediction.Prediction; -import io.openems.edge.predictor.api.test.DummyPredictor; -import io.openems.edge.predictor.api.test.DummyPredictorManager; import io.openems.edge.timedata.test.DummyTimedata; import io.openems.edge.timeofusetariff.test.DummyTimeOfUseTariffProvider; @@ -45,28 +32,21 @@ public void test() throws Exception { } /** - * Creates a {@link TimeOfUseTariffControllerImplTest} instance. + * Creates a {@link TimeOfUseTariffControllerImpl} instance. * * @param clock a {@link Clock} * @return the object * @throws Exception on error */ public static TimeOfUseTariffControllerImpl create(Clock clock) throws Exception { - var now = roundDownToQuarter(ZonedDateTime.now(clock)); - final var midnight = now.truncatedTo(DAYS); var componentManager = new DummyComponentManager(clock); var sum = new DummySum(); - var predictor0 = new DummyPredictor("predictor0", componentManager, - Prediction.from(sum, SUM_PRODUCTION, midnight, PRODUCTION_PREDICTION_QUARTERLY), SUM_PRODUCTION); - var predictor1 = new DummyPredictor("predictor0", componentManager, - Prediction.from(sum, SUM_CONSUMPTION, midnight, CONSUMPTION_PREDICTION_QUARTERLY), SUM_CONSUMPTION); - var timeOfUseTariff = DummyTimeOfUseTariffProvider.fromHourlyPrices(clock, TestData.HOURLY_PRICES_SUMMER); + var timeOfUseTariff = DummyTimeOfUseTariffProvider.empty(clock); var sut = new TimeOfUseTariffControllerImpl(); new ControllerTest(sut) // .addReference("cm", new DummyConfigurationAdmin()) // .addReference("componentManager", componentManager) // - .addReference("predictorManager", new DummyPredictorManager(predictor0, predictor1)) // .addReference("timedata", new DummyTimedata("timedata0")) // .addReference("timeOfUseTariff", timeOfUseTariff) // .addReference("sum", sum) // @@ -87,45 +67,4 @@ public static TimeOfUseTariffControllerImpl create(Clock clock) throws Exception .next(new TestCase()); return sut; } - - /** - * Gets the {@link Optimizer} via Java Reflection. - * - * @param ctrl the {@link TimeOfUseTariffControllerImplTest} - * @return the object - * @throws Exception on error - */ - public static Optimizer getOptimizer(TimeOfUseTariffControllerImpl ctrl) throws Exception { - var field = TimeOfUseTariffControllerImpl.class.getDeclaredField("optimizer"); - field.setAccessible(true); - return (Optimizer) field.get(ctrl); - } - - /** - * Calls the 'createParams()' method in the {@link Optimizer} via Java - * Reflection. - * - * @param optimizer the {@link Optimizer} - * @throws Exception on error - */ - public static void callCreateParams(Optimizer optimizer) throws Exception { - var method = Optimizer.class.getDeclaredMethod("createParams"); - method.setAccessible(true); - method.invoke(optimizer); - } - - /** - * Gets the {@link Context} via Java Reflection. - * - * @param ctrl the {@link TimeOfUseTariffControllerImplTest} - * @return the object - * @throws Exception on error - */ - @SuppressWarnings("unchecked") - public static Context getContext(TimeOfUseTariffControllerImpl ctrl) throws Exception { - var optimizer = getOptimizer(ctrl); - var field = Optimizer.class.getDeclaredField("context"); - field.setAccessible(true); - return ((Supplier) field.get(optimizer)).get(); - } } diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/UtilsTest.java b/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/UtilsTest.java new file mode 100644 index 00000000000..c7a104c3419 --- /dev/null +++ b/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/UtilsTest.java @@ -0,0 +1,215 @@ +package io.openems.edge.controller.ess.timeofusetariff; + +import static io.openems.edge.common.test.TestUtils.withValue; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; +import static io.openems.edge.controller.ess.timeofusetariff.Utils.calculateAutomaticMode; +import static io.openems.edge.controller.ess.timeofusetariff.Utils.calculateChargeGridPower; +import static io.openems.edge.controller.ess.timeofusetariff.Utils.calculateDelayDischargePower; +import static io.openems.edge.controller.ess.timeofusetariff.Utils.calculateEssChargeInChargeGridPowerFromParams; +import static io.openems.edge.controller.ess.timeofusetariff.Utils.calculateMaxChargeProductionPower; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import io.openems.edge.common.sum.DummySum; +import io.openems.edge.controller.ess.timeofusetariff.Utils.ApplyState; +import io.openems.edge.ess.api.SymmetricEss; +import io.openems.edge.ess.test.DummyHybridEss; +import io.openems.edge.ess.test.DummyManagedSymmetricEss; + +public class UtilsTest { + + @Test + public void testCalculateChargeGridPower() { + assertEquals(-10000, calculateChargeGridPower(null, // + new DummyManagedSymmetricEss("ess0") // + .withCapacity(20000), // + /* essActivePower */ -6000, // + /* gridActivePower */ 10000, // + /* maxChargePowerFromGrid */ 20000, // + /* limitChargePowerFor14aEnWG */ false)); + + assertEquals(-4200, calculateChargeGridPower(null, // + new DummyManagedSymmetricEss("ess0") // + .withCapacity(20000), // + /* essActivePower */ -6000, // + /* gridActivePower */ 10000, // + /* maxChargePowerFromGrid */ 20000, // + /* limitChargePowerFor14aEnWG */ true)); + + assertEquals(-11000, calculateChargeGridPower(null, // + new DummyManagedSymmetricEss("ess0") // + .withCapacity(20000), // + /* essActivePower */ -6000, // + /* gridActivePower */ 5000, // + /* maxChargePowerFromGrid */ 20000, // + /* limitChargePowerFor14aEnWG */ false)); + + assertEquals(-5860, calculateChargeGridPower(1340, // + new DummyManagedSymmetricEss("ess0"), // + /* essActivePower */ -1000, // + /* gridActivePower */ 500, // + /* maxChargePowerFromGrid */ 24000, // + /* limitChargePowerFor14aEnWG */ false)); + + // Would be -3584, but limited to 5000 which is already surpassed + // TODO if this should actually serve as blackout-protection, a positive value + // would have to be returned + assertEquals(0, calculateChargeGridPower(1000, // + new DummyManagedSymmetricEss("ess0"), // + /* essActivePower */ 1000, // + /* gridActivePower */ 9000, // + /* maxChargePowerFromGrid */ 5000, // + /* limitChargePowerFor14aEnWG */ false)); + + assertEquals(-8360, calculateChargeGridPower(1340, // + new DummyHybridEss("ess0") // + .withDcDischargePower(-1500), // + /* essActivePower */ -1000, // + /* gridActivePower */ -2000, // + /* maxChargePowerFromGrid */ 24000, // + /* limitChargePowerFor14aEnWG */ false)); + } + + @Test + public void testCalculateChargeProduction() { + assertEquals(-500, calculateMaxChargeProductionPower(// + new DummySum() // + .withProductionAcActivePower(500)) // + .intValue()); + + assertEquals(0, calculateMaxChargeProductionPower(// + new DummySum()) // + .intValue()); + + assertEquals(0, calculateMaxChargeProductionPower(// + new DummySum() // + .withProductionAcActivePower(-100 /* wrong */)) // + .intValue()); + } + + @Test + public void testCalculateDelayDischarge() { + // DC-PV + assertEquals(500, calculateDelayDischargePower(// + new DummyHybridEss("ess0") // + .withActivePower(-500) // + .withDcDischargePower(-1000))); + + // Never negative + assertEquals(0, calculateDelayDischargePower(// + new DummyHybridEss("ess0") // + .withActivePower(-1500) // + .withDcDischargePower(-1000))); + + // AC-PV + assertEquals(0, calculateDelayDischargePower(// + new DummyManagedSymmetricEss("ess0") // + .withActivePower(-1500))); + } + + @Test + public void testCalculateMaxChargeGridPowerFromParams() { + final var ess = new DummyManagedSymmetricEss("ess0"); + + // No params, initial ESS + assertEquals(0, calculateEssChargeInChargeGridPowerFromParams(null, ess)); + + // No params, ESS with MaxApparentPower + withValue(ess, SymmetricEss.ChannelId.MAX_APPARENT_POWER, 1000); + assertEquals(250, calculateEssChargeInChargeGridPowerFromParams(null, ess)); + + // No params, ESS with Capacity + withValue(ess, SymmetricEss.ChannelId.CAPACITY, 15000); + assertEquals(7500, calculateEssChargeInChargeGridPowerFromParams(null, ess)); + + // With params (22 kWh; but few Consumption) + assertEquals(5360, calculateEssChargeInChargeGridPowerFromParams(1340, ess)); + } + + @Test + public void testCalculateAutomaticMode() { + assertEquals("Null-Check", new ApplyState(BALANCING, null), // + calculateAutomaticMode(// + new DummySum(), // + new DummyManagedSymmetricEss("ess0"), // + /* essChargeInChargeGrid */ 1000, // + /* maxChargePowerFromGrid */ 2000, // + /* limitChargePowerFor14aEnWG */ true, // + BALANCING)); + assertEquals("Null-Check", new ApplyState(BALANCING, null), // + calculateAutomaticMode(// + new DummySum() // + .withGridActivePower(100), // + new DummyManagedSymmetricEss("ess0"), // + /* essChargeInChargeGrid */ 1000, // + /* maxChargePowerFromGrid */ 2000, // + /* limitChargePowerFor14aEnWG */ true, // + BALANCING)); + + assertEquals("BALANCING", new ApplyState(BALANCING, null), // + calculateAutomaticMode(// + new DummySum() // + .withGridActivePower(100), // + new DummyManagedSymmetricEss("ess0") // + .withActivePower(500), // + /* essChargeInChargeGrid */ 1000, // + /* maxChargePowerFromGrid */ 2000, // + /* limitChargePowerFor14aEnWG */ true, // + BALANCING)); + + assertEquals("DELAY_DISCHARGE stays DELAY_DISCHARGE", new ApplyState(DELAY_DISCHARGE, 0), // + calculateAutomaticMode(// + new DummySum() // + .withGridActivePower(100), // + new DummyManagedSymmetricEss("ess0") // + .withActivePower(500), // + /* essChargeInChargeGrid */ 1000, // + /* maxChargePowerFromGrid */ 2000, // + /* limitChargePowerFor14aEnWG */ true, // + DELAY_DISCHARGE)); + assertEquals("DELAY_DISCHARGE to BALANCING", new ApplyState(BALANCING, null), // + calculateAutomaticMode(// + new DummySum() // + .withGridActivePower(-500), // + new DummyManagedSymmetricEss("ess0") // + .withActivePower(500), // + /* essChargeInChargeGrid */ 1000, // + /* maxChargePowerFromGrid */ 2000, // + /* limitChargePowerFor14aEnWG */ true, // + DELAY_DISCHARGE)); + + assertEquals("CHARGE_GRID stays CHARGE_GRID", new ApplyState(CHARGE_GRID, -1400), // + calculateAutomaticMode(// + new DummySum() // + .withGridActivePower(100), // + new DummyManagedSymmetricEss("ess0") // + .withActivePower(500), // + /* essChargeInChargeGrid */ 1000, // + /* maxChargePowerFromGrid */ 2000, // + /* limitChargePowerFor14aEnWG */ true, // + CHARGE_GRID)); + assertEquals("CHARGE_GRID to DELAY_DISCHARGE", new ApplyState(DELAY_DISCHARGE, 0), // + calculateAutomaticMode(// + new DummySum() // + .withGridActivePower(100), // + new DummyManagedSymmetricEss("ess0") // + .withActivePower(500), // + /* essChargeInChargeGrid */ 1000, // + /* maxChargePowerFromGrid */ 400, // + /* limitChargePowerFor14aEnWG */ true, // + CHARGE_GRID)); + assertEquals("CHARGE_GRID to BALANCING", new ApplyState(BALANCING, null), // + calculateAutomaticMode(// + new DummySum() // + .withGridActivePower(-500), // + new DummyManagedSymmetricEss("ess0") // + .withActivePower(500), // + /* essChargeInChargeGrid */ 1000, // + /* maxChargePowerFromGrid */ 0, // + /* limitChargePowerFor14aEnWG */ true, // + CHARGE_GRID)); + } +} diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/ScheduleDatasTest.java b/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/ScheduleDatasTest.java deleted file mode 100644 index 0ee6b1f3b95..00000000000 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/ScheduleDatasTest.java +++ /dev/null @@ -1,134 +0,0 @@ -package io.openems.edge.controller.ess.timeofusetariff.jsonrpc; - -import static io.openems.common.utils.JsonUtils.prettyToString; -import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; -import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; -import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest.CLOCK; -import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest.callCreateParams; -import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest.getOptimizer; -import static io.openems.edge.controller.ess.timeofusetariff.jsonrpc.ScheduleDatas.fromLogString; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.time.ZoneId; -import java.time.ZonedDateTime; - -import org.junit.Test; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSortedMap; - -import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest; -import io.openems.edge.controller.ess.timeofusetariff.jsonrpc.ScheduleDatas.ScheduleData; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.EnergyFlow; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.Length; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.OptimizePeriod; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.QuarterPeriod; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator; - -public class ScheduleDatasTest { - - protected static final ZonedDateTime TIME = ZonedDateTime.of(2000, 1, 1, 0, 15, 0, 0, ZoneId.of("UTC")); - - protected static final ScheduleDatas SCHEDULE_DATAS = new ScheduleDatas(22_000, ImmutableList.of(// - new ScheduleData(TIME, null, 100, 200, 300, 1234, 222, 333, 78.9, DELAY_DISCHARGE, 987, 654), - new ScheduleData(TIME.plusMinutes(15), null, 100, 200, 300, 4567, 444, 333, 12.3, CHARGE_GRID, 987, 654))); - - @Test - public void testIsEmpty() { - assertFalse(SCHEDULE_DATAS.isEmpty()); - assertTrue(new ScheduleDatas(22_000, ImmutableList.of()).isEmpty()); - } - - @Test - public void testStream() { - assertEquals(2, SCHEDULE_DATAS.stream().count()); - } - - @Test - public void testToLogString() { - assertEquals( - """ - OPTIMIZER Time OptimizeBy EssMaxChargeEnergy EssMaxDischargeEnergy MaxBuyFromGrid EssInitial Production Consumption Price State EssChargeDischarge Grid - OPTIMIZER 00:15 - 100 200 300 1234 222 333 78.90 DELAY_DISCHARGE 987 654 - OPTIMIZER 00:30 - 100 200 300 4567 444 333 12.30 CHARGE_GRID 987 654 - """, - SCHEDULE_DATAS.toLogString("OPTIMIZER ")); - } - - @Test - public void testToJsonArray() { - assertEquals(""" - [ - { - "timestamp": "2000-01-01T00:00:00Z", - "soc": null, - "production": null, - "consumption": null, - "state": null, - "price": null, - "ess": null, - "grid": null - }, - { - "timestamp": "2000-01-01T00:15:00Z", - "soc": 6, - "production": 222, - "consumption": 333, - "state": 0, - "price": 78.9, - "ess": 987, - "grid": 654 - }, - { - "timestamp": "2000-01-01T00:30:00Z", - "soc": 21, - "production": 444, - "consumption": 333, - "state": 3, - "price": 12.3, - "ess": 987, - "grid": 654 - } - ]""", prettyToString(SCHEDULE_DATAS.toJsonArray(TIME.minusMinutes(15)))); - } - - @Test - public void testFromLogString() { - var log = SCHEDULE_DATAS.toLogString(""); - assertEquals(log, fromLogString(22_000, log).toLogString("")); - } - - @Test - public void testFromSchedule1() throws Exception { - var optimizer = getOptimizer(TimeOfUseTariffControllerImplTest.create(CLOCK)); - callCreateParams(optimizer); - var sds = ScheduleDatas.fromSchedule(optimizer); - assertEquals( - """ - Time OptimizeBy EssMaxChargeEnergy EssMaxDischargeEnergy MaxBuyFromGrid EssInitial Production Consumption Price State EssChargeDischarge Grid - """, - sds.toLogString("")); - } - - @Test - public void testFromSchedule2() throws Exception { - var sds = ScheduleDatas.fromSchedule(22_000, ImmutableSortedMap.of(// - TIME, // - new Simulator.Period(// - new OptimizePeriod(TIME, Length.QUARTER, 1, 2, 3, 4, 5, 6, 7., ImmutableList.of(// - new QuarterPeriod(TIME, 1, 2, 3, 4, 5, 6, 7))), - StateMachine.BALANCING, 10_000, - new EnergyFlow(0, 0, 1000 /* ess */, 500 /* grid */, 0, 0, 0, 0, 0, 0)) // - )); - assertEquals( - """ - OPTIMIZER Time OptimizeBy EssMaxChargeEnergy EssMaxDischargeEnergy MaxBuyFromGrid EssInitial Production Consumption Price State EssChargeDischarge Grid - OPTIMIZER 00:15 QUARTER 1 2 4 10000 5 6 7.00 BALANCING 1000 500 - """, - sds.toLogString("OPTIMIZER ")); - } - -} diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/OptimizerTest.java b/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/OptimizerTest.java deleted file mode 100644 index 310c8f74fc8..00000000000 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/OptimizerTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; - -import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest.CLOCK; -import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest.getOptimizer; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest; - -public class OptimizerTest { - - @Test - public void testEmpty() throws Exception { - var sut = getOptimizer(TimeOfUseTariffControllerImplTest.create(CLOCK)); - assertNull(sut.getParams()); - assertNull(sut.getCurrentStateMachine()); - assertTrue(sut.getSchedule().isEmpty()); - } - -} diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/UtilsTest.java b/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/UtilsTest.java deleted file mode 100644 index bd3309a40c5..00000000000 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/UtilsTest.java +++ /dev/null @@ -1,558 +0,0 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; - -import static io.openems.common.utils.DateUtils.roundDownToQuarter; -import static io.openems.common.utils.JsonUtils.getAsFloat; -import static io.openems.common.utils.JsonUtils.getAsInt; -import static io.openems.common.utils.JsonUtils.getAsJsonArray; -import static io.openems.common.utils.JsonUtils.getAsJsonObject; -import static io.openems.common.utils.UuidUtils.getNilUuid; -import static io.openems.edge.common.test.TestUtils.withValue; -import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; -import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; -import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.CONSUMPTION_PREDICTION_QUARTERLY; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PAST_HOURLY_PRICES; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PAST_SOC; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PAST_STATES; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PRODUCTION_888_20231106; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PRODUCTION_PREDICTION_QUARTERLY; -import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest.CLOCK; -import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest.callCreateParams; -import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest.getContext; -import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest.getOptimizer; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.EnergyFlowTest.NO_FLOW; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.SimulatorTest.TIME; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.SimulatorTest.createParams888d20231106; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.ESS_MAX_SOC; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_CONSUMPTION; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_ESS_DISCHARGE_POWER; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_ESS_SOC; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_GRID; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_PRODUCTION; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.calculateChargeGridPower; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.calculateDelayDischargePower; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.calculateEssChargeInChargeGridPowerFromParams; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.calculateExecutionLimitSeconds; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.calculateMaxChargeProductionPower; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.createSimulatorParams; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.findFirstPeakIndex; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.findFirstValleyIndex; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.generateProductionPrediction; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.getEssMinSocEnergy; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.handleGetScheduleRequest; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.interpolateArray; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.joinConsumptionPredictions; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.paramsAreValid; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.postprocessRunState; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.postprocessSimulatorState; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.toEnergy; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.toPower; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.updateSchedule; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.time.Duration; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.TreeMap; -import java.util.stream.IntStream; - -import org.junit.Test; - -import com.google.common.collect.ImmutableSortedMap; - -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.test.TimeLeapClock; -import io.openems.common.types.ChannelAddress; -import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.sum.DummySum; -import io.openems.edge.common.test.AbstractDummyOpenemsComponent; -import io.openems.edge.controller.ess.emergencycapacityreserve.ControllerEssEmergencyCapacityReserve; -import io.openems.edge.controller.ess.limittotaldischarge.ControllerEssLimitTotalDischarge; -import io.openems.edge.controller.ess.timeofusetariff.ControlMode; -import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImplTest; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.Period; -import io.openems.edge.ess.api.SymmetricEss; -import io.openems.edge.ess.test.DummyHybridEss; -import io.openems.edge.ess.test.DummyManagedSymmetricEss; -import io.openems.edge.timedata.test.DummyTimedata; - -public class UtilsTest { - - protected static ImmutableSortedMap prepareExistingSchedule(ZonedDateTime fromDate, - StateMachine... existingSchedule) { - return IntStream.range(0, existingSchedule.length) // - .mapToObj(Integer::valueOf) // - .collect(ImmutableSortedMap.toImmutableSortedMap( - ZonedDateTime::compareTo, // - i -> fromDate.plusMinutes(i * 15), // - i -> existingSchedule[i])); - } - - @Test - public void testCreateSimulatorParams() throws Exception { - final var context = getContext(TimeOfUseTariffControllerImplTest.create(CLOCK)); - final var p = createSimulatorParams(context, ImmutableSortedMap.of()); - final var op = p.optimizePeriods().get(0); - assertEquals(4, p.optimizePeriods().size()); - assertEquals(10000, p.essTotalEnergy()); - assertEquals(0, p.essMinSocEnergy()); - assertEquals(250, op.essMaxChargeEnergy()); - assertEquals(250, op.essMaxDischargeEnergy()); - assertEquals(6000, p.essInitialEnergy()); - assertEquals(434, op.essChargeInChargeGrid()); - assertEquals(2500, op.maxBuyFromGrid()); - assertArrayEquals(ControlMode.CHARGE_CONSUMPTION.states, p.states()); - } - - @Test - public void testInterpolateArrayFloat() { - assertArrayEquals(new double[] { 123, 123, 234, 234, 345 }, // - interpolateArray(new Double[] { null, 123., 234., null, 345., null }), // - 0.0001F); - - assertArrayEquals(new double[] {}, // - interpolateArray(new Double[] { null }), // - 0.0001F); - } - - @Test - public void testInterpolateArrayInteger() { - assertArrayEquals(new int[] { 123, 123, 234, 234, 345 }, // - interpolateArray(new Integer[] { null, 123, 234, null, 345, null })); - - assertArrayEquals(new int[] {}, // - interpolateArray(new Integer[] { null })); - - assertArrayEquals(new int[] { 123, 123 }, // - interpolateArray(new Integer[] { null, 123 })); - - assertArrayEquals(new int[] { 123 }, // - interpolateArray(new Integer[] { 123, null })); - } - - @Test - public void testToPower() { - assertEquals(2000, (int) toPower(500)); - assertNull(toPower(null)); - } - - @Test - public void testGenerateProductionPrediction() { - final var arr = new Integer[] { 1, 2, 3 }; - assertArrayEquals(arr, generateProductionPrediction(arr, 2)); - assertArrayEquals(new Integer[] { 1, 2, 3, 0 }, generateProductionPrediction(arr, 4)); - } - - @Test - public void testJoinConsumptionPredictions() { - assertArrayEquals(// - new Integer[] { 1, 2, 3, 4, 55, 66, 77, 88, 99 }, // - joinConsumptionPredictions(4, // - new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, // - new Integer[] { 11, 22, 33, 44, 55, 66, 77, 88, 99 })); - } - - @Test - public void testFindFirstPeakIndex() { - assertEquals(0, findFirstPeakIndex(0, new double[0])); - assertEquals(0, findFirstPeakIndex(0, new double[] { 1 })); - assertEquals(0, findFirstPeakIndex(0, new double[] { 1, 0 })); - assertEquals(1, findFirstPeakIndex(0, new double[] { 0, 1, 0 })); - assertEquals(1, findFirstPeakIndex(0, new double[] { 0, 1, 0, 1 })); - assertEquals(5, findFirstPeakIndex(5, new double[0])); - } - - @Test - public void testFindFirstValleyIndex() { - assertEquals(0, findFirstValleyIndex(0, new double[0])); - assertEquals(0, findFirstValleyIndex(0, new double[] { 1 })); - assertEquals(1, findFirstValleyIndex(0, new double[] { 1, 0 })); - assertEquals(0, findFirstValleyIndex(0, new double[] { 0, 1, 0 })); - assertEquals(2, findFirstValleyIndex(1, new double[] { 0, 1, 0, 1 })); - assertEquals(5, findFirstValleyIndex(5, new double[0])); - } - - @Test - public void testCalculateChargeGridPower() { - var params = createParams888d20231106(ControlMode.CHARGE_CONSUMPTION.states); - assertNull(calculateChargeGridPower(params, // - new DummyManagedSymmetricEss("ess0"), // - new DummySum(), // - /* maxChargePowerFromGrid */ 24_000, // - /* limitChargePowerFor14aEnWG */ false)); - - assertEquals(-10000, calculateChargeGridPower(null, // - new DummyManagedSymmetricEss("ess0") // - .withCapacity(20_000) // - .withActivePower(-6_000), // - new DummySum() // - .withGridActivePower(10_000), // - /* maxChargePowerFromGrid */ 20_000, // - /* limitChargePowerFor14aEnWG */ false).intValue()); - - assertEquals(-4200, calculateChargeGridPower(null, // - new DummyManagedSymmetricEss("ess0") // - .withCapacity(20_000) // - .withActivePower(-6_000), // - new DummySum() // - .withGridActivePower(10_000), // - /* maxChargePowerFromGrid */ 20_000, // - /* limitChargePowerFor14aEnWG */ true).intValue()); - - assertEquals(-11000, calculateChargeGridPower(null, // - new DummyManagedSymmetricEss("ess0") // - .withCapacity(20_000) // - .withActivePower(-6_000), // - new DummySum() // - .withGridActivePower(5_000), // - /* maxChargePowerFromGrid */ 20_000, // - /* limitChargePowerFor14aEnWG */ false).intValue()); - - assertEquals(-5860, calculateChargeGridPower(params, // - new DummyManagedSymmetricEss("ess0") // - .withActivePower(-1000), // - new DummySum() // - .withGridActivePower(500), // - /* maxChargePowerFromGrid */ 24_000, // - /* limitChargePowerFor14aEnWG */ false).intValue()); - - // Would be -3584, but limited to 5000 which is already surpassed - // TODO if this should actually serve as blackout-protection, a positive value - // would have to be returned - assertEquals(0, calculateChargeGridPower(params, // - new DummyManagedSymmetricEss("ess0") // - .withActivePower(1000), // - new DummySum() // - .withGridActivePower(9000), // - /* maxChargePowerFromGrid */ 5_000, // - /* limitChargePowerFor14aEnWG */ false).intValue()); - - assertEquals(-8360, calculateChargeGridPower(params, // - new DummyHybridEss("ess0") // - .withActivePower(-1000) // - .withDcDischargePower(-1500), // - new DummySum() // - .withGridActivePower(-2000), // - /* maxChargePowerFromGrid */ 24_000, // - /* limitChargePowerFor14aEnWG */ false).intValue()); - } - - @Test - public void testCalculateChargeProduction() { - assertEquals(-500, calculateMaxChargeProductionPower(// - new DummySum() // - .withProductionAcActivePower(500)) // - .intValue()); - - assertEquals(0, calculateMaxChargeProductionPower(// - new DummySum()) // - .intValue()); - - assertEquals(0, calculateMaxChargeProductionPower(// - new DummySum() // - .withProductionAcActivePower(-100 /* wrong */)) // - .intValue()); - } - - @Test - public void testCalculateDelayDischarge() { - // DC-PV - assertEquals(500, calculateDelayDischargePower(// - new DummyHybridEss("ess0") // - .withActivePower(-500) // - .withDcDischargePower(-1000)) - .intValue()); - - // Never negative - assertEquals(0, calculateDelayDischargePower(// - new DummyHybridEss("ess0") // - .withActivePower(-1500) // - .withDcDischargePower(-1000)) - .intValue()); - - // AC-PV - assertEquals(0, calculateDelayDischargePower(// - new DummyManagedSymmetricEss("ess0") // - .withActivePower(-1500)) // - .intValue()); - } - - @Test - public void testParamsAreValid() throws Exception { - var builder = Params.create() // - .setTime(TIME) // - .setEssInitialEnergy(0) // - .setEssTotalEnergy(22000) // - .setEssMinSocEnergy(2_000) // - .setEssMaxSocEnergy(20_000) // - .seMaxBuyFromGrid(toEnergy(24_000)) // - .seMaxBuyFromGrid(0) // - .setStates(new StateMachine[0]); - - // No periods are available - assertFalse(paramsAreValid(builder // - .setProductions() // - .setConsumptions() // - .setPrices() // - .build())); - - // Production and Consumption predictions are all zero - assertFalse(paramsAreValid(builder // - .setProductions(0, 0, 0) // - .setConsumptions(0, 0) // - .setPrices(123F) // - .build())); - - // Prices are all the same - assertFalse(paramsAreValid(builder // - .setProductions(0, 1, 3) // - .setConsumptions(0, 2) // - .setPrices(123F, 123F) // - .build())); - - // Finally got it right... - assertTrue(paramsAreValid(builder // - .setProductions(0, 1, 3) // - .setConsumptions(0, 2) // - .setPrices(123F, 124F) // - .build())); - assertEquals(2, builder.build().optimizePeriods().size()); - } - - private static class MyControllerEssLimitTotalDischarge - extends AbstractDummyOpenemsComponent - implements ControllerEssLimitTotalDischarge { - - protected MyControllerEssLimitTotalDischarge(Integer minSoc) { - super("ctrl0", // - OpenemsComponent.ChannelId.values(), // - ControllerEssLimitTotalDischarge.ChannelId.values() // - ); - withValue(this.getMinSocChannel(), minSoc); - } - - @Override - public void run() throws OpenemsNamedException { - } - - @Override - protected MyControllerEssLimitTotalDischarge self() { - return this; - } - } - - private static class MyControllerEssEmergencyCapacityReserve - extends AbstractDummyOpenemsComponent - implements ControllerEssEmergencyCapacityReserve { - - protected MyControllerEssEmergencyCapacityReserve(Integer reserveSoc) { - super("ctrl0", // - OpenemsComponent.ChannelId.values(), // - ControllerEssEmergencyCapacityReserve.ChannelId.values() // - ); - withValue(this.getActualReserveSocChannel(), reserveSoc); - } - - @Override - public void run() throws OpenemsNamedException { - } - - @Override - protected MyControllerEssEmergencyCapacityReserve self() { - return this; - } - } - - @Test - public void testGetEssMinSocEnergy() { - var t1 = new MyControllerEssLimitTotalDischarge(50); - var t2 = new MyControllerEssLimitTotalDischarge(null); - var t3 = new MyControllerEssEmergencyCapacityReserve(30); - assertEquals(5000, getEssMinSocEnergy(new Context(// - null, null, null, null, null, // - List.of(t3), List.of(t1, t2), // - null, 0, false), // - 10000)); - } - - @Test - public void testHandleScheduleRequest() throws Exception { - final var clock = new TimeLeapClock(Instant.parse("2020-03-04T14:19:00.00Z"), ZoneOffset.UTC); - final var ctrl = TimeOfUseTariffControllerImplTest.create(clock); - - // Simulate historic data - var now = roundDownToQuarter(ZonedDateTime.now(clock)); - final var fromDate = now.minusHours(3); - var timedata = new DummyTimedata("timedata0"); - for (var i = 0; i < 12; i++) { - var quarter = fromDate.plusMinutes(i * 15); - timedata.add(quarter, new ChannelAddress("ctrl0", "QuarterlyPrices"), PAST_HOURLY_PRICES[i]); - timedata.add(quarter, new ChannelAddress("ctrl0", "StateMachine"), PAST_STATES[i]); - timedata.add(quarter, SUM_PRODUCTION, PRODUCTION_PREDICTION_QUARTERLY[i]); - timedata.add(quarter, SUM_CONSUMPTION, CONSUMPTION_PREDICTION_QUARTERLY[i]); - timedata.add(quarter, SUM_ESS_SOC, PAST_SOC[i]); - timedata.add(quarter, SUM_ESS_DISCHARGE_POWER, PRODUCTION_888_20231106[i]); - timedata.add(quarter, SUM_GRID, PRODUCTION_888_20231106[i]); - } - - var optimizer = getOptimizer(ctrl); - callCreateParams(optimizer); - - // Testing only past data. For full data, optimizer has to be created as well. - var result = handleGetScheduleRequest(optimizer, getNilUuid(), timedata, "ctrl0", clock.now()).getResult(); - - // JsonUtils.prettyPrint(result); - - var schedule = getAsJsonArray(result, "schedule"); - assertEquals(11, schedule.size()); - { - var period = getAsJsonObject(schedule.get(0)); - assertEquals(PAST_HOURLY_PRICES[0], getAsFloat(period, "price"), 0.00F); - assertEquals(PRODUCTION_PREDICTION_QUARTERLY[0] / 4, getAsInt(period, "production")); - } - } - - @Test - public void testPostprocessPeriodState() { - var p = Params.create() // - .setTime(TIME) // - .setEssInitialEnergy(0) // - .setEssTotalEnergy(22000) // - .setEssMinSocEnergy(2_000) // - .setEssMaxSocEnergy(20_000) // - .setEssMaxChargeEnergy(0) // - .setEssMaxDischargeEnergy(0) // - .seMaxBuyFromGrid(toEnergy(24_000)) // - .setProductions() // - .setConsumptions() // - .setPrices(new double[] { 123 }) // - .setStates(new StateMachine[0]) // - .build(); - - assertEquals("BALANCING stays BALANCING", // - BALANCING, postprocessSimulatorState(p, 0, BALANCING, NO_FLOW)); - - assertEquals("DELAY_DISCHARGE but battery is empty", // - BALANCING, postprocessSimulatorState(p, 2000, DELAY_DISCHARGE, NO_FLOW)); - - assertEquals("DELAY_DISCHARGE and would discharge in balancing", // - DELAY_DISCHARGE, postprocessSimulatorState(p, 2001, DELAY_DISCHARGE, NO_FLOW)); - assertEquals("DELAY_DISCHARGE and would charge from PV in balancing", // - BALANCING, postprocessSimulatorState(p, 2001, DELAY_DISCHARGE, - new EnergyFlow(0, 0, 0, 0, 0, 0, 1 /* productionToEss */, 0, 0, 0))); - - assertEquals("CHARGE_GRID actually from grid", // - CHARGE_GRID, postprocessSimulatorState(p, 0, CHARGE_GRID, - new EnergyFlow(0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /* gridToEss */))); - assertEquals("CHARGE_GRID but actually not charging", // - BALANCING, postprocessSimulatorState(p, 0, CHARGE_GRID, NO_FLOW)); - assertEquals("CHARGE_GRID but battery is full", // - DELAY_DISCHARGE, postprocessSimulatorState(p, 20_001, CHARGE_GRID, - new EnergyFlow(0, 0, 0, 0, 0, 0, 0, 0, 0, 1 /* gridToEss */))); - } - - @Test - public void testPostprocessRunState() { - // SoC undefined -> all stay - assertEquals(BALANCING, postprocessRunState(0, null, 0, BALANCING)); - assertEquals(DELAY_DISCHARGE, postprocessRunState(0, null, 0, DELAY_DISCHARGE)); - assertEquals(CHARGE_GRID, postprocessRunState(0, null, 0, CHARGE_GRID)); - - assertEquals("BALANCING stays BALANCING", // - BALANCING, postprocessRunState(10, 10, 0, BALANCING)); - - assertEquals("DELAY_DISCHARGE but SoC is at Min-SoC", // - BALANCING, postprocessRunState(10, 10, 0, DELAY_DISCHARGE)); - assertEquals("DELAY_DISCHARGE and SoC is above Min-SoC", // - DELAY_DISCHARGE, postprocessRunState(10, 11, 0, DELAY_DISCHARGE)); - - assertEquals("CHARGE_GRID but SoC is at Max-SoC", // - DELAY_DISCHARGE, postprocessRunState((int) ESS_MAX_SOC, (int) ESS_MAX_SOC + 1, 0, CHARGE_GRID)); - assertEquals("CHARGE_GRID and SoC is below or equal Max-SoC", // - CHARGE_GRID, postprocessRunState((int) ESS_MAX_SOC, (int) ESS_MAX_SOC, 0, CHARGE_GRID)); - } - - @Test - public void testCalculateExecutionLimitSeconds() { - final var clock = new TimeLeapClock(Instant.parse("2022-01-01T00:00:00.00Z"), ZoneOffset.UTC); - assertEquals(Duration.ofMinutes(14).plusSeconds(30).toSeconds(), calculateExecutionLimitSeconds(clock)); - - clock.leap(11, ChronoUnit.MINUTES); - assertEquals(Duration.ofMinutes(3).plusSeconds(30).toSeconds(), calculateExecutionLimitSeconds(clock)); - - clock.leap(150, ChronoUnit.SECONDS); - assertEquals(60, calculateExecutionLimitSeconds(clock)); - - clock.leap(1, ChronoUnit.SECONDS); - assertEquals(Duration.ofMinutes(15).plusSeconds(59).toSeconds(), calculateExecutionLimitSeconds(clock)); - } - - @Test - public void testCalculateMaxChargeGridPowerFromParams() { - final var params = createParams888d20231106(ControlMode.CHARGE_CONSUMPTION.states); - final var ess = new DummyManagedSymmetricEss("ess0"); - - // No params, initial ESS - assertEquals(0, calculateEssChargeInChargeGridPowerFromParams(null, ess)); - - // No params, ESS with MaxApparentPower - withValue(ess, SymmetricEss.ChannelId.MAX_APPARENT_POWER, 1000); - assertEquals(250, calculateEssChargeInChargeGridPowerFromParams(null, ess)); - - // No params, ESS with Capacity - withValue(ess, SymmetricEss.ChannelId.CAPACITY, 15000); - assertEquals(7500, calculateEssChargeInChargeGridPowerFromParams(null, ess)); - - // With params (22 kWh; but few Consumption) - assertEquals(5360, calculateEssChargeInChargeGridPowerFromParams(params, ess)); - } - - @Test - public void testUpdateSchedule() { - final ZonedDateTime t = ZonedDateTime.of(2000, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC")); - final Period pOld = new Period(null, DELAY_DISCHARGE, 0, NO_FLOW); - final Period pNew = new Period(null, BALANCING, 0, NO_FLOW); - - var schedule = new TreeMap(); - schedule.put(t.minusMinutes(15), pOld); // old entry is removed - schedule.put(t, pOld); // current entry stays - schedule.put(t.plusMinutes(15), pOld); // is overridden - schedule.put(t.plusMinutes(30), pOld); // is overridden - schedule.put(t.plusMinutes(45), pOld); // timestamp is missing in new Schedule -> remove - - var newSchedule = ImmutableSortedMap.naturalOrder() // - .put(t, pNew) // - .put(t.plusMinutes(15), pNew) // - .put(t.plusMinutes(30), pNew) // - .build(); - - updateSchedule(t, schedule, newSchedule); - - // One old entry - assertEquals(1, schedule.values().stream().filter(v -> v == pOld).count()); - - // Two new entries - assertEquals(2, schedule.values().stream().filter(v -> v == pNew).count()); - - // No old entry - assertEquals(0, schedule.keySet().stream().filter(tz -> tz.isBefore(t)).count()); - - // Details - assertEquals(pOld, schedule.get(t)); - assertEquals(pNew, schedule.get(t.plusMinutes(15))); - assertEquals(pNew, schedule.get(t.plusMinutes(30))); - - // No current entry -> handle null - schedule.remove(t); - updateSchedule(t, schedule, newSchedule); - } -} diff --git a/io.openems.edge.controller.io.heatingelement/src/io/openems/edge/controller/io/heatingelement/ControllerIoHeatingElementImpl.java b/io.openems.edge.controller.io.heatingelement/src/io/openems/edge/controller/io/heatingelement/ControllerIoHeatingElementImpl.java index 11f32531716..3fddd82d2b0 100644 --- a/io.openems.edge.controller.io.heatingelement/src/io/openems/edge/controller/io/heatingelement/ControllerIoHeatingElementImpl.java +++ b/io.openems.edge.controller.io.heatingelement/src/io/openems/edge/controller/io/heatingelement/ControllerIoHeatingElementImpl.java @@ -219,8 +219,7 @@ protected Status modeAutomatic() throws IllegalArgumentException, OpenemsNamedEx targetLevel = this.applyHysteresis(targetLevel); Status runState; - runState = targetLevel.equals(Level.LEVEL_0) ? Status.INACTIVE - : Status.ACTIVE; + runState = targetLevel.equals(Level.LEVEL_0) ? Status.INACTIVE : Status.ACTIVE; var now = LocalTime.now(this.componentManager.getClock()); var configuredEndTime = DateUtils.parseLocalTimeOrError(this.config.endTime()); diff --git a/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/ControllerEssBalancingSchedule.java b/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/ControllerEssBalancingSchedule.java index c8fa907e10a..6fc7c43f953 100644 --- a/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/ControllerEssBalancingSchedule.java +++ b/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/ControllerEssBalancingSchedule.java @@ -9,10 +9,9 @@ import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; import io.openems.edge.controller.api.Controller; -public interface ControllerEssBalancingSchedule extends Controller, OpenemsComponent, JsonApi { +public interface ControllerEssBalancingSchedule extends Controller, OpenemsComponent { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { NO_ACTIVE_SETPOINT(Doc.of(Level.INFO) // diff --git a/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/ControllerEssBalancingScheduleImpl.java b/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/ControllerEssBalancingScheduleImpl.java index 02bcb5ef9e9..72a69f96798 100644 --- a/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/ControllerEssBalancingScheduleImpl.java +++ b/io.openems.edge.controller.symmetric.balancingschedule/src/io/openems/edge/controller/symmetric/balancingschedule/ControllerEssBalancingScheduleImpl.java @@ -3,7 +3,6 @@ import java.time.ZonedDateTime; import java.util.List; import java.util.Optional; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.CopyOnWriteArrayList; import org.osgi.service.cm.ConfigurationAdmin; @@ -21,14 +20,10 @@ import org.slf4j.LoggerFactory; import com.google.gson.JsonArray; -import com.google.gson.JsonObject; import io.openems.common.exceptions.InvalidValueException; -import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.jsonrpc.request.SetGridConnScheduleRequest; import io.openems.common.jsonrpc.request.SetGridConnScheduleRequest.GridConnSchedule; import io.openems.common.session.Role; @@ -36,8 +31,9 @@ import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; -import io.openems.edge.common.user.User; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.EdgeGuards; +import io.openems.edge.common.jsonapi.JsonApiBuilder; import io.openems.edge.controller.api.Controller; import io.openems.edge.ess.api.ManagedSymmetricEss; import io.openems.edge.meter.api.ElectricityMeter; @@ -49,7 +45,7 @@ configurationPolicy = ConfigurationPolicy.REQUIRE // ) public class ControllerEssBalancingScheduleImpl extends AbstractOpenemsComponent - implements ControllerEssBalancingSchedule, Controller, OpenemsComponent, JsonApi { + implements ControllerEssBalancingSchedule, Controller, OpenemsComponent, ComponentJsonApi { private final Logger log = LoggerFactory.getLogger(ControllerEssBalancingScheduleImpl.class); @@ -163,32 +159,14 @@ public void run() throws OpenemsNamedException { } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException { - user.assertRoleIsAtLeast("handleJsonrpcRequest", Role.OWNER); - - switch (request.getMethod()) { - - case SetGridConnScheduleRequest.METHOD: - return this.handleSetGridConnScheduleRequest(user, SetGridConnScheduleRequest.from(request)); - - default: - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } - } - - /** - * Handles a SetGridConnScheduleRequest. - * - * @param user the User - * @param request the SetGridConnScheduleRequest - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - private CompletableFuture handleSetGridConnScheduleRequest(User user, - SetGridConnScheduleRequest request) { - this.schedule = request.getSchedule(); - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId(), new JsonObject())); + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(SetGridConnScheduleRequest.METHOD, def -> { + def.setGuards(EdgeGuards.roleIsAtleast(Role.OWNER)); + }, call -> { + final var request = SetGridConnScheduleRequest.from(call.getRequest()); + this.schedule = request.getSchedule(); + return new GenericJsonrpcResponseSuccess(request.getId()); + }); } /** diff --git a/io.openems.edge.core/bnd.bnd b/io.openems.edge.core/bnd.bnd index 670793f4591..58ebb64e861 100644 --- a/io.openems.edge.core/bnd.bnd +++ b/io.openems.edge.core/bnd.bnd @@ -16,6 +16,7 @@ Bundle-Version: 1.0.0.${tstamp} io.openems.edge.predictor.api,\ io.openems.edge.scheduler.api,\ io.openems.edge.timedata.api,\ + io.openems.edge.timeofusetariff.api,\ io.openems.wrapper.fastexcel,\ io.openems.wrapper.okhttp,\ io.openems.wrapper.sdnotify,\ diff --git a/io.openems.edge.core/src/io/openems/edge/app/evcs/DezonyEvcs.java b/io.openems.edge.core/src/io/openems/edge/app/evcs/DezonyEvcs.java index acb60aef5e5..88bcc11c66e 100644 --- a/io.openems.edge.core/src/io/openems/edge/app/evcs/DezonyEvcs.java +++ b/io.openems.edge.core/src/io/openems/edge/app/evcs/DezonyEvcs.java @@ -148,7 +148,8 @@ protected ThrowingTriFunction, L return AppConfiguration.create() // .addTask(Tasks.component(components)) // - .addTask(Tasks.schedulerByCentralOrder(new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // + .addTask(Tasks.schedulerByCentralOrder( + new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // .addDependencies(EvcsCluster.dependency(t, this.componentManager, this.componentUtil, maxHardwarePowerPerPhase, evcsId)) // .build(); diff --git a/io.openems.edge.core/src/io/openems/edge/app/evcs/IesKeywattEvcs.java b/io.openems.edge.core/src/io/openems/edge/app/evcs/IesKeywattEvcs.java index 9b339212730..95a1fab272a 100644 --- a/io.openems.edge.core/src/io/openems/edge/app/evcs/IesKeywattEvcs.java +++ b/io.openems.edge.core/src/io/openems/edge/app/evcs/IesKeywattEvcs.java @@ -154,7 +154,8 @@ protected ThrowingTriFunction, L return AppConfiguration.create() // .addTask(Tasks.component(components)) // - .addTask(Tasks.schedulerByCentralOrder(new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // + .addTask(Tasks.schedulerByCentralOrder( + new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // .addDependencies(EvcsCluster.dependency(t, this.componentManager, this.componentUtil, maxHardwarePowerPerPhase, evcsId)) // .build(); diff --git a/io.openems.edge.core/src/io/openems/edge/app/evcs/KebaEvcs.java b/io.openems.edge.core/src/io/openems/edge/app/evcs/KebaEvcs.java index bf1344b7b9e..e9a680cb831 100644 --- a/io.openems.edge.core/src/io/openems/edge/app/evcs/KebaEvcs.java +++ b/io.openems.edge.core/src/io/openems/edge/app/evcs/KebaEvcs.java @@ -141,7 +141,8 @@ protected ThrowingTriFunction, L return AppConfiguration.create() // .addTask(Tasks.component(components)) // - .addTask(Tasks.schedulerByCentralOrder(new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // + .addTask(Tasks.schedulerByCentralOrder( + new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // .throwingOnlyIf(ip.startsWith("192.168.25."), b -> b.addTask(Tasks.staticIp(new InterfaceConfiguration("eth0") // .addIp("Evcs", "192.168.25.10/24")))) // diff --git a/io.openems.edge.core/src/io/openems/edge/app/evcs/WebastoNextEvcs.java b/io.openems.edge.core/src/io/openems/edge/app/evcs/WebastoNextEvcs.java index 32044ba31d6..8530681aae6 100644 --- a/io.openems.edge.core/src/io/openems/edge/app/evcs/WebastoNextEvcs.java +++ b/io.openems.edge.core/src/io/openems/edge/app/evcs/WebastoNextEvcs.java @@ -161,7 +161,8 @@ protected ThrowingTriFunction, L return AppConfiguration.create() // .addTask(Tasks.component(components)) // - .addTask(Tasks.schedulerByCentralOrder(new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // + .addTask(Tasks.schedulerByCentralOrder( + new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // .addDependencies(EvcsCluster.dependency(t, this.componentManager, this.componentUtil, maxHardwarePowerPerPhase, evcsId)) // .build(); diff --git a/io.openems.edge.core/src/io/openems/edge/app/evcs/WebastoUniteEvcs.java b/io.openems.edge.core/src/io/openems/edge/app/evcs/WebastoUniteEvcs.java index 8966672689a..9a3f9e9dff1 100644 --- a/io.openems.edge.core/src/io/openems/edge/app/evcs/WebastoUniteEvcs.java +++ b/io.openems.edge.core/src/io/openems/edge/app/evcs/WebastoUniteEvcs.java @@ -161,7 +161,8 @@ protected ThrowingTriFunction, L return AppConfiguration.create() // .addTask(Tasks.component(components)) // - .addTask(Tasks.schedulerByCentralOrder(new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // + .addTask(Tasks.schedulerByCentralOrder( + new SchedulerComponent(ctrlEvcsId, "Controller.Evcs", this.getAppId()))) // .addDependencies(EvcsCluster.dependency(t, this.componentManager, this.componentUtil, maxHardwarePowerPerPhase, evcsId)) // .build(); diff --git a/io.openems.edge.core/src/io/openems/edge/app/integratedsystem/FeneconHome20.java b/io.openems.edge.core/src/io/openems/edge/app/integratedsystem/FeneconHome20.java index c5fa2b7406e..083bd11673b 100644 --- a/io.openems.edge.core/src/io/openems/edge/app/integratedsystem/FeneconHome20.java +++ b/io.openems.edge.core/src/io/openems/edge/app/integratedsystem/FeneconHome20.java @@ -5,6 +5,7 @@ import static io.openems.edge.app.integratedsystem.FeneconHomeComponents.battery; import static io.openems.edge.app.integratedsystem.FeneconHomeComponents.batteryInverter; import static io.openems.edge.app.integratedsystem.FeneconHomeComponents.charger; +import static io.openems.edge.app.integratedsystem.FeneconHomeComponents.chargerOld; import static io.openems.edge.app.integratedsystem.FeneconHomeComponents.ctrlEmergencyCapacityReserve; import static io.openems.edge.app.integratedsystem.FeneconHomeComponents.ctrlEssSurplusFeedToGrid; import static io.openems.edge.app.integratedsystem.FeneconHomeComponents.emergencyMeter; @@ -78,6 +79,7 @@ import io.openems.edge.core.appmanager.dependency.aggregatetask.SchedulerByCentralOrderConfiguration.SchedulerComponent; import io.openems.edge.core.appmanager.formly.Exp; import io.openems.edge.core.appmanager.formly.JsonFormlyUtil; +import io.openems.edge.core.appmanager.formly.expression.BooleanExpression; /** * Describes a FENECON Home 20 energy storage system. @@ -97,8 +99,10 @@ "CT_RATIO_FIRST": 200, "HAS_AC_METER": false, "AC_METER_TYPE": {@link AcMeterType}, - "HAS_PV_[1-4]":true, - "PV_ALIAS_[1-4]":"PV [1-4]", + "HAS_PV_[1-4]":true, // deprecated + "PV_ALIAS_[1-4]":"PV [1-4]", // deprecated + "HAS_MPPT_[1-2]":true, + "MPPT_ALIAS_[1-2]":"MPPT [1-2]", "HAS_EMERGENCY_RESERVE":true, "EMERGENCY_RESERVE_ENABLED":true, "EMERGENCY_RESERVE_SOC":20, @@ -178,9 +182,17 @@ public Type self() { } + @Deprecated private static final int MAX_NUMBER_OF_PV = 4; + @Deprecated private static final IntFunction HAS_PV = value -> "HAS_PV_" + (value + 1); + @Deprecated private static final IntFunction PV_ALIAS = value -> "ALIAS_PV_" + (value + 1); + + private static final int MAX_NUMBER_OF_MPPT = 2; + private static final IntFunction HAS_MPPT = value -> "HAS_MPPT_" + (value + 1); + private static final IntFunction MPPT_ALIAS = value -> "ALIAS_MPPT_" + (value + 1); + private final Map pvDefs = new TreeMap<>(); @Activate @@ -192,21 +204,55 @@ public FeneconHome20(// ) { super(componentManager, componentContext, cm, componentUtil); + BooleanExpression anyOldPvSelected = null; for (int i = 0; i < MAX_NUMBER_OF_PV; i++) { final var oneBased = i + 1; final var hasPv = new ParentPropertyImpl(HAS_PV.apply(i), AppDef.copyOfGeneric(defaultDef(), def -> def // .setTranslatedLabel("App.IntegratedSystem.hasPv.label", oneBased, (oneBased + 1) / 2) // .setDefaultValue(false) // - .setField(JsonFormlyUtil::buildCheckboxFromNameable) // )); + hasPv.def().setField(t -> JsonFormlyUtil.buildCheckboxFromNameable(t), + (app, property, l, parameter, field) -> { + field.onlyShowIf(Exp.currentModelValue(hasPv).notNull()); + }); + + if (anyOldPvSelected == null) { + anyOldPvSelected = Exp.currentModelValue(hasPv).isNull(); + } else { + anyOldPvSelected = anyOldPvSelected.and(Exp.currentModelValue(hasPv).isNull()); + } + final var pvAlias = new ParentPropertyImpl(PV_ALIAS.apply(i), AppDef.copyOfGeneric(defaultDef(), def -> def // .setTranslatedLabel("App.IntegratedSystem.pvAlias.label", oneBased) // .setDefaultValueString((app, property, l, parameter) -> TranslationUtil .getTranslation(parameter.bundle(), "App.IntegratedSystem.pvAlias.alias", oneBased)) // .setField(JsonFormlyUtil::buildInputFromNameable, (app, property, l, parameter, field) -> { field.onlyShowIf(Exp.currentModelValue(hasPv).notNull()); + }))); + + this.pvDefs.put(hasPv.name(), hasPv); + this.pvDefs.put(pvAlias.name(), pvAlias); + } + final var tempAnyOldPvSelected = anyOldPvSelected; + + for (int i = 0; i < MAX_NUMBER_OF_MPPT; i++) { + final var oneBased = i + 1; + final var hasPv = new ParentPropertyImpl(HAS_MPPT.apply(i), AppDef.copyOfGeneric(defaultDef(), def -> def // + .setTranslatedLabel("App.IntegratedSystem.hasMppt.label", oneBased) // + .setDefaultValue(false) // + .setField(JsonFormlyUtil::buildCheckboxFromNameable, (app, property, l, parameter, field) -> { + field.onlyShowIf(tempAnyOldPvSelected); }) // )); + final var pvAlias = new ParentPropertyImpl(MPPT_ALIAS.apply(i), + AppDef.copyOfGeneric(defaultDef(), def -> def // + .setTranslatedLabel("App.IntegratedSystem.mpptAlias.label", oneBased) // + .setDefaultValueString((app, property, l, parameter) -> TranslationUtil.getTranslation( + parameter.bundle(), "App.IntegratedSystem.mpptAlias.alias", oneBased)) // + .setField(JsonFormlyUtil::buildInputFromNameable, (app, property, l, parameter, field) -> { + field.onlyShowIf(Exp.currentModelValue(hasPv).notNull()); + }) // + )); this.pvDefs.put(hasPv.name(), hasPv); this.pvDefs.put(pvAlias.name(), pvAlias); @@ -279,6 +325,16 @@ protected ThrowingTriFunction self() { } + @Deprecated private static final int MAX_NUMBER_OF_PV = 6; + @Deprecated private static final IntFunction HAS_PV = value -> "HAS_PV_" + (value + 1); + @Deprecated private static final IntFunction PV_ALIAS = value -> "ALIAS_PV_" + (value + 1); + + private static final int MAX_NUMBER_OF_MPPT = 3; + private static final IntFunction HAS_MPPT = value -> "HAS_MPPT_" + (value + 1); + private static final IntFunction MPPT_ALIAS = value -> "ALIAS_MPPT_" + (value + 1); + private final Map pvDefs = new TreeMap<>(); @Activate @@ -192,13 +204,24 @@ public FeneconHome30(// ) { super(componentManager, componentContext, cm, componentUtil); + BooleanExpression anyOldPvSelected = null; for (int i = 0; i < MAX_NUMBER_OF_PV; i++) { final var oneBased = i + 1; final var hasPv = new ParentPropertyImpl(HAS_PV.apply(i), AppDef.copyOfGeneric(defaultDef(), def -> def // .setTranslatedLabel("App.IntegratedSystem.hasPv.label", oneBased, (oneBased + 1) / 2) // .setDefaultValue(false) // - .setField(JsonFormlyUtil::buildCheckboxFromNameable) // )); + hasPv.def().setField(t -> JsonFormlyUtil.buildCheckboxFromNameable(t), + (app, property, l, parameter, field) -> { + field.onlyShowIf(Exp.currentModelValue(hasPv).notNull()); + }); + + if (anyOldPvSelected == null) { + anyOldPvSelected = Exp.currentModelValue(hasPv).isNull(); + } else { + anyOldPvSelected = anyOldPvSelected.and(Exp.currentModelValue(hasPv).isNull()); + } + final var pvAlias = new ParentPropertyImpl(PV_ALIAS.apply(i), AppDef.copyOfGeneric(defaultDef(), def -> def // .setTranslatedLabel("App.IntegratedSystem.pvAlias.label", oneBased) // .setDefaultValueString((app, property, l, parameter) -> TranslationUtil @@ -208,6 +231,30 @@ public FeneconHome30(// }) // )); + this.pvDefs.put(hasPv.name(), hasPv); + this.pvDefs.put(pvAlias.name(), pvAlias); + } + final var tempAnyOldPvSelected = anyOldPvSelected; + + for (int i = 0; i < MAX_NUMBER_OF_MPPT; i++) { + final var oneBased = i + 1; + final var hasPv = new ParentPropertyImpl(HAS_MPPT.apply(i), AppDef.copyOfGeneric(defaultDef(), def -> def // + .setTranslatedLabel("App.IntegratedSystem.hasMppt.label", oneBased) // + .setDefaultValue(false) // + .setField(JsonFormlyUtil::buildCheckboxFromNameable, (app, property, l, parameter, field) -> { + field.onlyShowIf(tempAnyOldPvSelected); + }) // + )); + final var pvAlias = new ParentPropertyImpl(MPPT_ALIAS.apply(i), + AppDef.copyOfGeneric(defaultDef(), def -> def // + .setTranslatedLabel("App.IntegratedSystem.mpptAlias.label", oneBased) // + .setDefaultValueString((app, property, l, parameter) -> TranslationUtil.getTranslation( + parameter.bundle(), "App.IntegratedSystem.mpptAlias.alias", oneBased)) // + .setField(JsonFormlyUtil::buildInputFromNameable, (app, property, l, parameter, field) -> { + field.onlyShowIf(Exp.currentModelValue(hasPv).notNull()); + }) // + )); + this.pvDefs.put(hasPv.name(), hasPv); this.pvDefs.put(pvAlias.name(), pvAlias); } @@ -279,6 +326,16 @@ protected ThrowingTriFunction def// .setTranslatedLabelWithAppPrefix(".filterForHome.label") // .setTranslatedDescriptionWithAppPrefix(".filterForHome.description") // - .setDefaultValue((app, property, l, parameter) -> JsonNull.INSTANCE) + .setDefaultValue("") // .setField(JsonFormlyUtil::buildInputFromNameable, (app, property, l, parameter, field) -> { - field.onlyShowIf(Exp.currentModelValue(MULTIPLE_HOMES_CHECK).notNull() - .or(Exp.currentModelValue(property).notNull())); + field.onlyShowIf(Exp.currentModelValue(MULTIPLE_HOMES_CHECK).notNull()); }) // .bidirectional(TIME_OF_USE_TARIFF_PROVIDER_ID, "filter", ComponentManagerSupplier::getComponentManager))); @@ -146,7 +144,8 @@ protected ThrowingTriFunction, L final var alias = this.getString(p, l, Property.ALIAS); final var accessToken = this.getValueOrDefault(p, Property.ACCESS_TOKEN, null); - final var filter = this.getValueOrDefault(p, Property.FILTER, null); + final var multipleHomesCheck = this.getBoolean(p, Property.MULTIPLE_HOMES_CHECK); + final var filter = multipleHomesCheck ? this.getString(p, Property.FILTER) : ""; final var components = Lists.newArrayList(// new EdgeConfig.Component(ctrlEssTimeOfUseTariffId, alias, "Controller.Ess.Time-Of-Use-Tariff", @@ -158,7 +157,7 @@ protected ThrowingTriFunction, L .onlyIf(accessToken != null && !accessToken.equals("xxx"), b -> { b.addProperty("accessToken", accessToken); }) // - .addPropertyIfNotNull("filter", filter) // + .addProperty("filter", filter) // .build())// ); diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppAssistant.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppAssistant.java index ce001806fdd..6532e61a7be 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppAssistant.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppAssistant.java @@ -1,8 +1,10 @@ package io.openems.edge.core.appmanager; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; + import com.google.gson.JsonArray; -import com.google.gson.JsonObject; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; public class AppAssistant { @@ -89,16 +91,21 @@ private AppAssistant(String name, String alias, JsonArray fields) { } /** - * Gets this {@link AppAssistant} as {@link JsonObject}. - * - * @return the {@link JsonObject} + * Returns a {@link JsonSerializer} for a {@link AppAssistant}. + * + * @return the created {@link JsonSerializer} */ - public JsonObject toJsonObject() { - return JsonUtils.buildJsonObject() // - .addProperty("name", this.name) // - .addProperty("alias", this.alias) // - .add("fields", this.fields) // - .build(); + public static JsonSerializer serializer() { + return jsonObjectSerializer(AppAssistant.class, // + json -> new AppAssistant(// + json.getString("name"), // + json.getString("alias"), // + json.getJsonArray("fields")), // + obj -> JsonUtils.buildJsonObject() // + .addProperty("name", obj.name) // + .addProperty("alias", obj.alias) // + .add("fields", obj.fields) // + .build()); } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppCenterBackendUtilImpl.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppCenterBackendUtilImpl.java index b01715345f1..d27333d5d14 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppCenterBackendUtilImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppCenterBackendUtilImpl.java @@ -33,7 +33,7 @@ import io.openems.common.jsonrpc.response.AppCenterIsKeyApplicableResponse; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.user.User; -import io.openems.edge.controller.api.backend.ControllerApiBackend; +import io.openems.edge.controller.api.backend.api.ControllerApiBackend; @Component public class AppCenterBackendUtilImpl implements AppCenterBackendUtil { @@ -105,7 +105,7 @@ public boolean isConnected() { private final CompletableFuture handleRequestAsync(User user, JsonrpcRequest request) throws OpenemsNamedException { - return this.getBackendOrError().handleJsonrpcRequest(user, new AppCenterRequest(request)) // + return this.getBackendOrError().sendRequest(user, new AppCenterRequest(request)) // .orTimeout(30L, TimeUnit.SECONDS); } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppDescriptor.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppDescriptor.java index 17d500fd1af..ff3e9a3525a 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppDescriptor.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppDescriptor.java @@ -1,9 +1,9 @@ package io.openems.edge.core.appmanager; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; import static io.openems.common.utils.StringUtils.definedOrElse; -import com.google.gson.JsonObject; - +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; public class AppDescriptor { @@ -46,14 +46,20 @@ public final String getWebsiteUrl() { } /** - * Builds a {@link JsonObject} out of this {@link AppDescriptor}. - * - * @return the {@link JsonObject} + * Returns a {@link JsonSerializer} for a {@link AppDescriptor}. + * + * @return the created {@link JsonSerializer} */ - public JsonObject toJsonObject() { - return JsonUtils.buildJsonObject() // - .addPropertyIfNotNull("websiteUrl", this.websiteUrl) // - .build(); + public static JsonSerializer serializer() { + return jsonObjectSerializer(AppDescriptor.class, json -> { + return new AppDescriptor(// + json.getString("websiteUrl") // + ); + }, obj -> { + return JsonUtils.buildJsonObject() // + .addPropertyIfNotNull("websiteUrl", obj.websiteUrl) // + .build(); + }); } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppManager.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppManager.java index 46bbe033d65..73749f40d64 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppManager.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppManager.java @@ -5,12 +5,11 @@ import io.openems.edge.common.channel.StateChannel; import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; /** * A Service that manages OpenEMS Apps. */ -public interface AppManager extends OpenemsComponent, JsonApi { +public interface AppManager extends OpenemsComponent { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { WRONG_APP_CONFIGURATION(Doc.of(Level.WARNING) // diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppManagerImpl.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppManagerImpl.java index 0784dfa08ab..e06ac96c3a3 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppManagerImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/AppManagerImpl.java @@ -1,5 +1,7 @@ package io.openems.edge.core.appmanager; +import static java.util.Collections.emptyList; + import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; @@ -35,16 +37,13 @@ import org.slf4j.LoggerFactory; import com.google.gson.JsonArray; +import com.google.gson.JsonObject; -import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.function.ThrowingConsumer; import io.openems.common.function.ThrowingFunction; import io.openems.common.function.ThrowingSupplier; -import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest.Property; import io.openems.common.oem.OpenemsEdgeOem; @@ -54,7 +53,10 @@ import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.EdgeGuards; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApiBuilder; import io.openems.edge.common.user.User; import io.openems.edge.core.appmanager.dependency.AppManagerAppHelper; import io.openems.edge.core.appmanager.dependency.Dependency; @@ -68,7 +70,6 @@ import io.openems.edge.core.appmanager.jsonrpc.GetApps; import io.openems.edge.core.appmanager.jsonrpc.UpdateAppInstance; import io.openems.edge.core.appmanager.validator.Validator; -import io.openems.edge.core.componentmanager.ComponentManagerImpl; @Designate(ocd = Config.class, factory = false) @Component(// @@ -77,9 +78,9 @@ property = { // "enabled=true" // }) -public class AppManagerImpl extends AbstractOpenemsComponent implements AppManager, OpenemsComponent, JsonApi { +public class AppManagerImpl extends AbstractOpenemsComponent implements AppManager, OpenemsComponent, ComponentJsonApi { - private final Logger log = LoggerFactory.getLogger(this.getClass()); + private final Logger log = LoggerFactory.getLogger(AppManagerImpl.class); @Reference private AppValidateWorker appValidateWorker; @@ -508,31 +509,31 @@ private final OpenemsAppInstance createInstanceWithFilledProperties(// * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - public CompletableFuture handleAddAppInstanceRequest(// + public AddAppInstance.Response handleAddAppInstanceRequest(// final User user, // nullable final AddAppInstance.Request request, // final boolean ignoreBackend // ) throws OpenemsNamedException { // check if key is valid for this app - if (!ignoreBackend && !this.backendUtil.isKeyApplicable(user, request.key, request.appId)) { + if (!ignoreBackend && !this.backendUtil.isKeyApplicable(user, request.key(), request.appId())) { throw new OpenemsException("Key not applicable!"); } - final var openemsApp = this.findAppByIdOrError(request.appId); + final var openemsApp = this.findAppByIdOrError(request.appId()); return this.lockModifyingApps(() -> { // initial check if the app can even be installed final var language = user == null ? Language.DEFAULT : user.getLanguage(); - openemsApp.getAppConfiguration(ConfigurationTarget.ADD, request.properties, language); + openemsApp.getAppConfiguration(ConfigurationTarget.ADD, request.properties(), language); this.validator.checkStatus(openemsApp, language); List warnings = new ArrayList<>(); - var instance = new OpenemsAppInstance(openemsApp.getAppId(), request.alias, UUID.randomUUID(), - request.properties, null); + var instance = new OpenemsAppInstance(openemsApp.getAppId(), request.alias(), UUID.randomUUID(), + request.properties(), null); if (!ignoreBackend) { try { // try to send the backend the install request - this.backendUtil.addInstallAppInstanceHistory(user, request.key, request.appId, + this.backendUtil.addInstallAppInstanceHistory(user, request.key(), request.appId(), instance.instanceId); } catch (OpenemsNamedException e) { // if timeout happens but the backend registered the app as installed it may @@ -564,8 +565,7 @@ public CompletableFuture handleAddAppInstanceRequest(// this.instantiatedApps.add(instance); } var instanceWithFilledProperties = this.createInstanceWithFilledProperties(openemsApp, instance); - return new Pair<>(true, CompletableFuture.completedFuture(// - new AddAppInstance.Response(request.id, instanceWithFilledProperties, warnings))); + return new Pair<>(true, new AddAppInstance.Response(instanceWithFilledProperties, warnings)); }, (shouldUpdate) -> { if (shouldUpdate == null || !shouldUpdate) { return; @@ -590,8 +590,8 @@ public CompletableFuture handleAddAppInstanceRequest(// * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - public CompletableFuture handleAddAppInstanceRequest(User user, - AddAppInstance.Request request) throws OpenemsNamedException { + public AddAppInstance.Response handleAddAppInstanceRequest(User user, AddAppInstance.Request request) + throws OpenemsNamedException { return this.handleAddAppInstanceRequest(user, request, false); } @@ -603,10 +603,10 @@ public CompletableFuture handleAddAppInstanceRequest(Us * @return the request id * @throws OpenemsNamedException on error */ - public CompletableFuture handleDeleteAppInstanceRequest(User user, - DeleteAppInstance.Request request) throws OpenemsNamedException { + public DeleteAppInstance.Response handleDeleteAppInstanceRequest(User user, DeleteAppInstance.Request request) + throws OpenemsNamedException { final var updatedResultPair = this.>lockModifyingApps(() -> { - final var instance = this.findInstanceById(request.instanceId).orElse(null); + final var instance = this.findInstanceById(request.instanceId()).orElse(null); if (instance == null) { return new Pair<>(false, null); } @@ -626,12 +626,12 @@ public CompletableFuture handleDeleteAppInstan try { this.updateAppManagerConfiguration(user, this.instantiatedApps); } catch (OpenemsNamedException e) { - throw new OpenemsException("Unable to update App-Manager configuration for ID [" + request.instanceId + throw new OpenemsException("Unable to update App-Manager configuration for ID [" + request.instanceId() + "]: " + e.getMessage()); } }); if (updatedResultPair == null) { - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.id)); + return new DeleteAppInstance.Response(emptyList()); } final var updatedResult = updatedResultPair.first; @@ -648,10 +648,9 @@ public CompletableFuture handleDeleteAppInstan this._setAppsNotSyncedWithBackend(true); }); if (updatedResult == null) { - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.id)); + return new DeleteAppInstance.Response(emptyList()); } else { - return CompletableFuture - .completedFuture(new DeleteAppInstance.Response(request.id, updatedResult.warnings)); + return new DeleteAppInstance.Response(updatedResult.warnings); } } @@ -663,17 +662,12 @@ public CompletableFuture handleDeleteAppInstan * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleGetAppAssistantRequest(// + private GetAppAssistant.Response handleGetAppAssistantRequest(// final User user, // final GetAppAssistant.Request request // ) throws OpenemsNamedException { - for (var app : this.availableApps) { - if (request.appId.equals(app.getAppId())) { - return CompletableFuture - .completedFuture(new GetAppAssistant.Response(request.id, app.getAppAssistant(user))); - } - } - throw new OpenemsException("App-ID [" + request.appId + "] is unknown"); + final var app = this.findAppByIdOrError(request.appId()); + return new GetAppAssistant.Response(app.getAppAssistant(user)); } /** @@ -684,11 +678,10 @@ private CompletableFuture handleGetAppAssistantRequest(/ * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleGetAppDescriptorRequest(User user, - GetAppDescriptor.Request request) throws OpenemsNamedException { - final var app = this.findAppByIdOrError(request.appId); - return CompletableFuture - .completedFuture(new GetAppDescriptor.Response(request.id, app.getAppDescriptor(this.oem))); + private GetAppDescriptor.Response handleGetAppDescriptorRequest(User user, GetAppDescriptor.Request request) + throws OpenemsNamedException { + final var app = this.findAppByIdOrError(request.appId()); + return new GetAppDescriptor.Response(app.getAppDescriptor(this.oem)); } /** @@ -699,10 +692,10 @@ private CompletableFuture handleGetAppDescriptorRequest( * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleGetAppInstancesRequest(User user, - GetAppInstances.Request request) throws OpenemsNamedException { + private GetAppInstances.Response handleGetAppInstancesRequest(User user, GetAppInstances.Request request) + throws OpenemsNamedException { var instances = this.instantiatedApps.stream() // - .filter(i -> i.appId.equals(request.appId)) // + .filter(i -> i.appId.equals(request.appId())) // .map(t -> { final var app = this.findAppById(t.appId).orElse(null); var properties = t.properties; @@ -710,8 +703,9 @@ private CompletableFuture handleGetAppInstancesRequest(U properties = AbstractOpenemsApp.fillUpProperties(app, properties); } return new OpenemsAppInstance(t.appId, t.alias, t.instanceId, properties, t.dependencies); - }); - return CompletableFuture.completedFuture(new GetAppInstances.Response(request.id, instances)); + }) // + .toList(); + return new GetAppInstances.Response(instances); } /** @@ -722,13 +716,12 @@ private CompletableFuture handleGetAppInstancesRequest(U * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleGetAppRequest(User user, GetApp.Request request) - throws OpenemsNamedException { - var app = this.availableApps.stream().filter(t -> t.getAppId().equals(request.appId)).findFirst().get(); - var instances = this.instantiatedApps.stream().filter(t -> t.appId.equals(request.appId)) - .collect(Collectors.toList()); - return CompletableFuture - .completedFuture(new GetApp.Response(request.id, app, instances, user.getLanguage(), this.validator)); + private GetApp.Response handleGetAppRequest(User user, GetApp.Request request) throws OpenemsNamedException { + final var app = this.findAppByIdOrError(request.appId()); + var instances = this.instantiatedApps.stream() // + .filter(t -> t.appId.equals(request.appId())) // + .toList(); + return GetApp.Response.newInstance(app, instances, user.getLanguage(), this.validator); } /** @@ -739,46 +732,103 @@ private CompletableFuture handleGetAppRequest(User user, * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleGetAppsRequest(User user, GetApps.Request request) - throws OpenemsNamedException { - return CompletableFuture.completedFuture(new GetApps.Response(request.id, this.availableApps, - this.instantiatedApps, user.getRole(), user.getLanguage(), this.validator)); + private GetApps.Response handleGetAppsRequest(User user, GetApps.Request request) throws OpenemsNamedException { + return GetApps.Response.newInstance(this.availableApps, this.instantiatedApps, user.getRole(), + user.getLanguage(), this.validator); } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException { - user.assertRoleIsAtLeast("handleJsonrpcRequest", Role.OWNER); + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(new GetApps(), endpoint -> { + endpoint.setDescription(""" + Gets all available apps on the current edge. + """.stripIndent()); + + endpoint.applyRequestBuilder(request -> { + request.addExample(new GetApps.Request()); + }); - switch (request.getMethod()) { + }, call -> this.handleGetAppsRequest(call.get(EdgeKeys.USER_KEY), call.getRequest())); - case GetApps.METHOD: - return this.handleGetAppsRequest(user, GetApps.Request.from(request)); + builder.handleRequest(new GetApp(), endpoint -> { + endpoint.setDescription(""" + Gets an app by its id. + """.stripIndent()); - case GetApp.METHOD: - return this.handleGetAppRequest(user, GetApp.Request.from(request)); + endpoint.applyRequestBuilder(request -> { + request.addExample("Get Keba app", new GetApp.Request("App.Evcs.Keba")); + }); - case GetAppAssistant.METHOD: - return this.handleGetAppAssistantRequest(user, GetAppAssistant.Request.from(request)); + }, call -> this.handleGetAppRequest(call.get(EdgeKeys.USER_KEY), call.getRequest())); - case GetAppDescriptor.METHOD: - return this.handleGetAppDescriptorRequest(user, GetAppDescriptor.Request.from(request)); + builder.handleRequest(new GetAppAssistant(), endpoint -> { + endpoint.setDescription(""" + Gets the AppAssistant for a app. + """.stripIndent()); - case GetAppInstances.METHOD: - return this.handleGetAppInstancesRequest(user, GetAppInstances.Request.from(request)); + endpoint.applyRequestBuilder(request -> { + request.addExample("Get the AppAssistant for Keba app", new GetAppAssistant.Request("App.Evcs.Keba")); + }); - case AddAppInstance.METHOD: - return this.handleAddAppInstanceRequest(user, AddAppInstance.Request.from(request), false); + }, call -> this.handleGetAppAssistantRequest(call.get(EdgeKeys.USER_KEY), call.getRequest())); - case UpdateAppInstance.METHOD: - return this.handleUpdateAppInstanceRequest(user, UpdateAppInstance.Request.from(request)); + builder.handleRequest(new GetAppDescriptor(), endpoint -> { + endpoint.setDescription(""" + Gets the AppDescriptor for a app. + """.stripIndent()); - case DeleteAppInstance.METHOD: - return this.handleDeleteAppInstanceRequest(user, DeleteAppInstance.Request.from(request)); + endpoint.applyRequestBuilder(request -> { + request.addExample("Get the AppDescriptor for Keba app", new GetAppDescriptor.Request("App.Evcs.Keba")); + }); - default: - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } + }, call -> this.handleGetAppDescriptorRequest(call.get(EdgeKeys.USER_KEY), call.getRequest())); + + builder.handleRequest(new GetAppInstances(), endpoint -> { + endpoint.setDescription(""" + Gets the AppInstances for a app. + """.stripIndent()); + + endpoint.applyRequestBuilder(request -> { + request.addExample("Get the instances of the Keba app", new GetAppInstances.Request("App.Evcs.Keba")); + }); + + }, call -> this.handleGetAppInstancesRequest(call.get(EdgeKeys.USER_KEY), call.getRequest())); + + builder.handleRequest(new UpdateAppInstance(), endpoint -> { + endpoint.setDescription(""" + Updates a AppInstance. + """.stripIndent()); + + endpoint.setGuards(EdgeGuards.roleIsAtleast(Role.OWNER)); + + }, call -> this.handleUpdateAppInstanceRequest(call.get(EdgeKeys.USER_KEY), call.getRequest())); + + builder.handleRequest(new DeleteAppInstance(), endpoint -> { + endpoint.setDescription(""" + Deletes a AppInstance. + """.stripIndent()) // + .setGuards(EdgeGuards.roleIsAtleast(Role.INSTALLER)); + + }, call -> this.handleDeleteAppInstanceRequest(call.get(EdgeKeys.USER_KEY), call.getRequest())); + + builder.handleRequest(new AddAppInstance(), endpoint -> { + endpoint.setDescription(""" + Handles a AddAppInstance Request. + """.stripIndent()); + endpoint.setGuards(EdgeGuards.roleIsAtleast(Role.OWNER)); + + endpoint.applyRequestBuilder(request -> { + request.addExample(new AddAppInstance.Request("0000-0000-0000-0000", "App.Id", "alias", + JsonUtils.buildJsonObject() // + .addProperty("key", "value") // + .build())); + }); + endpoint.applyResponseBuilder(response -> { + response.addExample(new AddAppInstance.Response( + new OpenemsAppInstance("App.Id", "alias", UUID.randomUUID(), new JsonObject(), emptyList()), + emptyList())); + }); + }, call -> this.handleAddAppInstanceRequest(call.get(EdgeKeys.USER_KEY), call.getRequest())); } /** @@ -789,15 +839,15 @@ public CompletableFuture handleJsonrpcRequest( * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - public CompletableFuture handleUpdateAppInstanceRequest(User user, - UpdateAppInstance.Request request) throws OpenemsNamedException { + public UpdateAppInstance.Response handleUpdateAppInstanceRequest(User user, UpdateAppInstance.Request request) + throws OpenemsNamedException { return this.lockModifyingApps(() -> { - final var oldApp = this.findInstanceByIdOrError(request.instanceId); + final var oldApp = this.findInstanceByIdOrError(request.instanceId()); final var app = this.findAppByIdOrError(oldApp.appId); - app.getAppConfiguration(ConfigurationTarget.UPDATE, request.properties, user.getLanguage()); + app.getAppConfiguration(ConfigurationTarget.UPDATE, request.properties(), user.getLanguage()); - final var updatedInstance = new OpenemsAppInstance(oldApp.appId, request.alias, oldApp.instanceId, - request.properties, oldApp.dependencies); + final var updatedInstance = new OpenemsAppInstance(oldApp.appId, request.alias(), oldApp.instanceId, + request.properties(), oldApp.dependencies); var result = this.lastUpdate = this.useAppManagerAppHelper(appHelper -> { return appHelper.updateApp(user, oldApp, updatedInstance, app); @@ -807,9 +857,9 @@ public CompletableFuture handleUpdateAppInstanceRequ // replace old instances with new ones this.instantiatedApps.removeAll(result.modifiedOrCreatedApps); this.instantiatedApps.addAll(result.modifiedOrCreatedApps); - return new Pair<>(true, CompletableFuture.completedFuture(// - new UpdateAppInstance.Response(request.id, // - this.createInstanceWithFilledProperties(app, result.rootInstance), result.warnings))); + + return new Pair<>(true, new UpdateAppInstance.Response( + this.createInstanceWithFilledProperties(app, result.rootInstance), result.warnings)); }, (shouldUpdate) -> { if (shouldUpdate == null || !shouldUpdate) { return; @@ -817,7 +867,7 @@ public CompletableFuture handleUpdateAppInstanceRequ try { this.updateAppManagerConfiguration(user, this.instantiatedApps); } catch (OpenemsNamedException e) { - throw new OpenemsException("Unable to update App-Manager configuration for ID [" + request.instanceId + throw new OpenemsException("Unable to update App-Manager configuration for ID [" + request.instanceId() + "]: " + e.getMessage()); } }); @@ -834,13 +884,9 @@ private void updateAppManagerConfiguration(User user, List a this.waitingForModified = true; AppManagerImpl.sortApps(apps); var p = new Property("apps", getJsonAppsString(apps)); - var updateRequest = new UpdateComponentConfigRequest(SINGLETON_COMPONENT_ID, Arrays.asList(p)); // user can be null using internal method - if (user == null) { - ((ComponentManagerImpl) this.componentManager).handleUpdateComponentConfigRequest(user, updateRequest); - } else { - this.componentManager.handleJsonrpcRequest(user, updateRequest); - } + this.componentManager.handleUpdateComponentConfigRequest(user, + new UpdateComponentConfigRequest(SINGLETON_COMPONENT_ID, Arrays.asList(p))); } private static void sortApps(List apps) { diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/ComponentUtilImpl.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/ComponentUtilImpl.java index 2cc1648bc68..46e6d79ebaa 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/ComponentUtilImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/ComponentUtilImpl.java @@ -41,9 +41,8 @@ import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.host.Host; -import io.openems.edge.common.jsonapi.JsonApi; import io.openems.edge.common.user.User; -import io.openems.edge.core.componentmanager.ComponentManagerImpl; +import io.openems.edge.core.host.HostImpl; import io.openems.edge.core.host.NetworkInterface; import io.openems.edge.core.host.jsonrpc.SetNetworkConfigRequest; import io.openems.edge.io.api.DigitalOutput; @@ -548,8 +547,8 @@ public String[] getPreferredRelays(// @Override public void updateInterfaces(User user, List> interfaces) throws OpenemsNamedException { - JsonApi host = this.componentManager.getComponent(Host.SINGLETON_COMPONENT_ID); - host.handleJsonrpcRequest(user, new SetNetworkConfigRequest(interfaces)); + HostImpl host = this.componentManager.getComponent(Host.SINGLETON_COMPONENT_ID); + host.handleSetNetworkConfigRequest(user, new SetNetworkConfigRequest(interfaces)); // wait until its updated do { @@ -628,12 +627,7 @@ public synchronized void setSchedulerComponentIds(// new UpdateComponentConfigRequest.Property("controllers.ids", ids) // )); - if (user != null) { - this.componentManager.handleJsonrpcRequest(user, request).get(); - return; - } - - ((ComponentManagerImpl) this.componentManager).handleUpdateComponentConfigRequest(user, request).get(); + this.componentManager.handleUpdateComponentConfigRequest(user, request); } catch (Exception e) { e.printStackTrace(); throw new OpenemsException("Could not update Scheduler!"); diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/OpenemsAppInstance.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/OpenemsAppInstance.java index 7240d9a6c67..45cc193805c 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/OpenemsAppInstance.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/OpenemsAppInstance.java @@ -1,11 +1,17 @@ package io.openems.edge.core.appmanager; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; +import static io.openems.common.utils.JsonUtils.toJsonArray; +import static java.util.Collections.emptyList; + import java.util.List; import java.util.Objects; import java.util.UUID; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; import io.openems.edge.core.appmanager.dependency.Dependency; @@ -27,7 +33,7 @@ public OpenemsAppInstance(String appId, String alias, UUID instanceId, JsonObjec this.alias = alias; this.instanceId = Objects.requireNonNull(instanceId); this.properties = properties; - this.dependencies = dependencies; + this.dependencies = dependencies == null ? emptyList() : dependencies; } @Override @@ -47,21 +53,39 @@ public int hashCode() { return Objects.hash(this.instanceId); } + /** + * Returns a {@link JsonSerializer} for a {@link OpenemsAppInstance}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(OpenemsAppInstance.class, // + json -> new OpenemsAppInstance(// + json.getString("appId"), // + json.getString("alias"), // + json.getStringPath("instanceId").getAsUuid(), // + json.getJsonObject("properties"), // + // TODO add optional methods + json.getList("dependencies", Dependency.serializer())), // + obj -> JsonUtils.buildJsonObject() // + .addProperty("appId", obj.appId) // + .addProperty("alias", obj.alias != null ? obj.alias : "") // + .addProperty("instanceId", obj.instanceId.toString()) // + .add("properties", obj.properties == null ? new JsonObject() : obj.properties) // + .onlyIf(obj.dependencies != null && !obj.dependencies.isEmpty(), b -> b // + .add("dependencies", obj.dependencies.stream() // + .map(Dependency.serializer()::serialize) // + .collect(toJsonArray()))) // + .build()); + } + /** * Gets this {@link OpenemsAppInstance} as {@link JsonObject}. * * @return the {@link JsonObject} */ - public JsonObject toJsonObject() { - return JsonUtils.buildJsonObject() // - .addProperty("appId", this.appId) // - .addProperty("alias", this.alias != null ? this.alias : "") // - .addProperty("instanceId", this.instanceId.toString()) // - // TODO define if the field is editable - .add("properties", this.properties == null ? new JsonObject() : this.properties) // - .onlyIf(this.dependencies != null && !this.dependencies.isEmpty(), j -> j.add("dependencies", // - this.dependencies.stream().map(Dependency::toJsonObject).collect(JsonUtils.toJsonArray()))) - .build(); + public JsonElement toJsonObject() { + return serializer().serialize(this); } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/ResolveDependencies.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/ResolveDependencies.java index e7222904914..a14ae6a2bea 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/ResolveDependencies.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/ResolveDependencies.java @@ -1,7 +1,6 @@ package io.openems.edge.core.appmanager; import java.util.List; -import java.util.concurrent.ExecutionException; import org.osgi.framework.BundleContext; import org.slf4j.Logger; @@ -105,16 +104,15 @@ public static void resolveDependencies(User user, AppManagerImpl appManagerImpl, try { LOG.info(String.format("Resolving dependency with installing %s!", config.appId)); - var future = appManagerImpl.handleAddAppInstanceRequest(user, // + appManagerImpl.handleAddAppInstanceRequest(user, // new AddAppInstance.Request(// config.appId, "key", // config.alias, // config.initialProperties), true); - future.get(); resolveDependencies(user, appManagerImpl, appManagerUtil); return; - } catch (OpenemsNamedException | InterruptedException | ExecutionException e) { + } catch (OpenemsNamedException e) { e.printStackTrace(); } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/Dependency.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/Dependency.java index 71ed57c8aff..acb45b0508c 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/Dependency.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/Dependency.java @@ -1,9 +1,12 @@ package io.openems.edge.core.appmanager.dependency; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; + import java.util.UUID; import com.google.gson.JsonObject; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; import io.openems.edge.core.appmanager.AppManager; @@ -35,4 +38,20 @@ public JsonObject toJsonObject() { .build(); } + /** + * Returns a {@link JsonSerializer} for a {@link Dependency}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(Dependency.class, // + json -> new Dependency(// + json.getString("key"), // + json.getStringPath("instanceId").getAsUuid()), // + obj -> JsonUtils.buildJsonObject() // + .addProperty("key", obj.key) // + .addProperty("instanceId", obj.instanceId.toString()) // + .build()); + } + } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/aggregatetask/ComponentAggregateTaskImpl.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/aggregatetask/ComponentAggregateTaskImpl.java index 4ac9a8b6e3f..b7c8349e4ed 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/aggregatetask/ComponentAggregateTaskImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/aggregatetask/ComponentAggregateTaskImpl.java @@ -26,7 +26,6 @@ import io.openems.edge.core.appmanager.ComponentUtilImpl; import io.openems.edge.core.appmanager.TranslationUtil; import io.openems.edge.core.appmanager.dependency.AppManagerAppHelperImpl; -import io.openems.edge.core.componentmanager.ComponentManagerImpl; @Component(// service = { // @@ -180,13 +179,8 @@ public void delete(User user, List otherAppConfigurations) thr } try { - final var request = new DeleteComponentConfigRequest(comp.getId()); - if (user != null) { - this.componentManager.handleJsonrpcRequest(user, request); - } else { - // user can be null using internal method - ((ComponentManagerImpl) this.componentManager).handleDeleteComponentConfigRequest(user, request); - } + this.componentManager.handleDeleteComponentConfigRequest(user, + new DeleteComponentConfigRequest(comp.getId())); this.deletedComponents.add(comp.getId()); } catch (OpenemsNamedException e) { errors.add(e.toString()); @@ -247,13 +241,8 @@ private void createComponent(User user, EdgeConfig.Component comp) throws Openem properties.add(new Property("id", comp.getId())); properties.add(new Property("alias", comp.getAlias())); - var request = new CreateComponentConfigRequest(comp.getFactoryId(), properties); - if (user != null) { - this.componentManager.handleJsonrpcRequest(user, request); - return; - } - // user can be null using internal method - ((ComponentManagerImpl) this.componentManager).handleCreateComponentConfigRequest(user, request); + this.componentManager.handleCreateComponentConfigRequest(user, + new CreateComponentConfigRequest(comp.getFactoryId(), properties)); } /** @@ -276,14 +265,9 @@ private void reconfigure(User user, EdgeConfig.Component myComp, EdgeConfig.Comp .map(t -> new Property(t.getKey(), t.getValue())) // .collect(Collectors.toList()); properties.add(new Property("alias", myComp.getAlias())); - var updateRequest = new UpdateComponentConfigRequest(actualComp.getId(), properties); - if (user != null) { - this.componentManager.handleJsonrpcRequest(user, updateRequest); - return; - } - // user can be null using internal method - ((ComponentManagerImpl) this.componentManager).handleUpdateComponentConfigRequest(user, updateRequest); + this.componentManager.handleUpdateComponentConfigRequest(user, + new UpdateComponentConfigRequest(actualComp.getId(), properties)); } @Override diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/aggregatetask/PersistencePredictorAggregateTaskImpl.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/aggregatetask/PersistencePredictorAggregateTaskImpl.java index 6b4995c0aef..49bfb12f7ed 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/aggregatetask/PersistencePredictorAggregateTaskImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/dependency/aggregatetask/PersistencePredictorAggregateTaskImpl.java @@ -8,7 +8,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.concurrent.ExecutionException; import java.util.stream.Stream; import org.osgi.service.component.annotations.Activate; @@ -144,15 +143,12 @@ private static void updatePredictor(// existingChannels.addAll(channelsToAdd); existingChannels.removeAll(channelsToRemove); - try { - componentManager.handleJsonrpcRequest(user, new UpdateComponentConfigRequest(predictor.id(), List.of(// - new UpdateComponentConfigRequest.Property("channelAddresses", existingChannels.stream() // - .map(JsonPrimitive::new) // - .collect(toJsonArray())) // - ))).get(); - } catch (InterruptedException | ExecutionException e) { - throw new OpenemsException("Unable to update Persistence Predictor", e); - } + componentManager.handleUpdateComponentConfigRequest(user, + new UpdateComponentConfigRequest(predictor.id(), List.of(// + new UpdateComponentConfigRequest.Property("channelAddresses", existingChannels.stream() // + .map(JsonPrimitive::new) // + .collect(toJsonArray())) // + ))); } private static Set getAllChannels(List otherAppConfigurations) { diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/formly/JsonFormlyUtil.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/formly/JsonFormlyUtil.java index 9b6dac90949..5c91ac7cefd 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/formly/JsonFormlyUtil.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/formly/JsonFormlyUtil.java @@ -146,7 +146,7 @@ public static > RangeBuilder buildRange(T property) { public static RangeBuilder buildRangeFromNameable(Nameable nameable) { return new RangeBuilder(nameable); } - + /** * Creates a JsonObject Formly DateTime Builder for the given enum. * @@ -156,7 +156,7 @@ public static RangeBuilder buildRangeFromNameable(Nameable nameable) { public static DateTimeBuilder buildDateTimeFromNameable(Nameable nameable) { return new DateTimeBuilder(nameable); } - + /** * Creates a JsonObject Formly Repeat Builder for the given enum. * diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/AddAppInstance.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/AddAppInstance.java index 6eb47e5c592..17c21023811 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/AddAppInstance.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/AddAppInstance.java @@ -1,18 +1,20 @@ package io.openems.edge.core.appmanager.jsonrpc; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; +import static io.openems.common.utils.JsonUtils.toJsonArray; + import java.util.List; -import java.util.Optional; -import java.util.UUID; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.serialization.JsonElementPath; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.EndpointRequestType; import io.openems.edge.core.appmanager.OpenemsAppInstance; +import io.openems.edge.core.appmanager.jsonrpc.AddAppInstance.Request; +import io.openems.edge.core.appmanager.jsonrpc.AddAppInstance.Response; /** * Adds an {@link OpenemsAppInstance}. @@ -46,81 +48,73 @@ * } * */ -public class AddAppInstance { - - public static final String METHOD = "addAppInstance"; - - public static class Request extends JsonrpcRequest { +public class AddAppInstance implements EndpointRequestType { - /** - * Parses a generic {@link JsonrpcRequest} to a {@link AddAppInstance}. - * - * @param r the {@link JsonrpcRequest} - * @return the {@link AddAppInstance} Request - * @throws OpenemsNamedException on error - */ - public static Request from(JsonrpcRequest r) throws OpenemsNamedException { - var p = r.getParams(); - var key = JsonUtils.getAsString(p, "key"); - var appId = JsonUtils.getAsString(p, "appId"); - var alias = JsonUtils.getAsOptionalString(p, "alias").orElse(null); - var properties = JsonUtils.getAsJsonObject(p, "properties"); - return new Request(r, key, appId, alias, properties); - } + @Override + public String getMethod() { + return "addAppInstance"; + } - public final String key; + @Override + public JsonSerializer getRequestSerializer() { + return Request.serializer(); + } - public final String appId; - public final String alias; - public final JsonObject properties; + @Override + public JsonSerializer getResponseSerializer() { + return Response.serializer(); + } - private Request(JsonrpcRequest request, String key, String appId, String alias, JsonObject properties) { - super(request, METHOD); - this.key = key; - this.appId = appId; - this.alias = alias; - this.properties = properties; - } + public static record Request(// + String appId, // + String key, // + String alias, // + JsonObject properties // + ) { - public Request(String appId, String key, String alias, JsonObject properties) { - super(METHOD); - this.key = key; - this.appId = appId; - this.alias = alias; - this.properties = properties; + /** + * Returns a {@link JsonSerializer} for a {@link AddAppInstance.Request}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(Request.class, // + json -> new Request(// + json.getString("appId"), // + json.getString("key"), // + json.getString("alias"), // + json.getJsonObject("properties")), + obj -> JsonUtils.buildJsonObject() // + .addProperty("appId", obj.appId()) // + .addProperty("key", obj.key()) // + .addProperty("alias", obj.alias()) // + .add("properties", obj.properties()) // + .build()); } - @Override - public JsonObject getParams() { - return JsonUtils.buildJsonObject() // - .addProperty("appId", this.appId) // - .addPropertyIfNotNull("alias", this.alias) // - .add("properties", this.properties) // - .build(); - } } - public static class Response extends JsonrpcResponseSuccess { - - public final OpenemsAppInstance instance; - public final JsonArray warnings; + public record Response(// + OpenemsAppInstance instance, // + List warnings // + ) { - public Response(UUID id, OpenemsAppInstance instance, List warnings) { - super(id); - this.instance = instance; - this.warnings = Optional.ofNullable(warnings) // - .map(t -> t.stream() // - .map(JsonPrimitive::new) // - .collect(JsonUtils.toJsonArray())) - .orElse(new JsonArray()); - } - - @Override - public JsonObject getResult() { - return JsonUtils.buildJsonObject() // - .add("instance", this.instance.toJsonObject()) // - .add("warnings", this.warnings) // - .build(); + /** + * Returns a {@link JsonSerializer} for a {@link AddAppInstance.Response}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(Response.class, // + json -> new Response(// + json.getElement("instance", OpenemsAppInstance.serializer()), // + json.getList("warnings", JsonElementPath::getAsString)), // + obj -> JsonUtils.buildJsonObject() // + .add("instance", obj.instance.toJsonObject()) // + .add("warnings", obj.warnings.stream() // + .map(JsonPrimitive::new) // + .collect(toJsonArray())) // + .build()); } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/DeleteAppInstance.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/DeleteAppInstance.java index 3806cd6339a..f3023bb117b 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/DeleteAppInstance.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/DeleteAppInstance.java @@ -1,17 +1,20 @@ package io.openems.edge.core.appmanager.jsonrpc; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; +import static io.openems.common.utils.JsonUtils.toJsonArray; + import java.util.List; import java.util.UUID; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.serialization.JsonElementPath; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.EndpointRequestType; import io.openems.edge.core.appmanager.OpenemsAppInstance; +import io.openems.edge.core.appmanager.jsonrpc.DeleteAppInstance.Request; +import io.openems.edge.core.appmanager.jsonrpc.DeleteAppInstance.Response; /** * Updates an {@link OpenemsAppInstance}.. @@ -43,61 +46,63 @@ * } * */ -public class DeleteAppInstance { - - public static final String METHOD = "deleteAppInstance"; +public final class DeleteAppInstance implements EndpointRequestType { - public static class Request extends JsonrpcRequest { + @Override + public String getMethod() { + return "deleteAppInstance"; + } - /** - * Parses a generic {@link JsonrpcRequest} to a {@link DeleteAppInstance}. - * - * @param r the {@link JsonrpcRequest} - * @return the {@link DeleteAppInstance} - * @throws OpenemsNamedException on error - */ - public static Request from(JsonrpcRequest r) throws OpenemsNamedException { - var p = r.getParams(); - var instanceId = JsonUtils.getAsUUID(p, "instanceId"); - return new Request(r, instanceId); - } + @Override + public JsonSerializer getRequestSerializer() { + return Request.serializer(); + } - public final UUID instanceId; + @Override + public JsonSerializer getResponseSerializer() { + return Response.serializer(); + } - private Request(JsonrpcRequest request, UUID instanceId) { - super(request, METHOD); - this.instanceId = instanceId; - } + public record Request(// + UUID instanceId // + ) { - public Request(UUID instanceId) { - super(METHOD); - this.instanceId = instanceId; + /** + * Returns a {@link JsonSerializer} for a {@link DeleteAppInstance.Request}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(DeleteAppInstance.Request.class, // + json -> new DeleteAppInstance.Request(// + json.getStringPath("instanceId").getAsUuid()), // + obj -> JsonUtils.buildJsonObject() // + .addProperty("instanceId", obj.instanceId().toString()) // + .build()); } - @Override - public JsonObject getParams() { - return JsonUtils.buildJsonObject() // - .addProperty("instanceId", this.instanceId.toString()) // - .build(); - } } - public static class Response extends JsonrpcResponseSuccess { + public record Response(// + List warnings // + ) { - private final JsonArray warnings; - - public Response(UUID id, List warnings) { - super(id); - this.warnings = warnings == null ? new JsonArray() - : warnings.stream().map(JsonPrimitive::new).collect(JsonUtils.toJsonArray()); + /** + * Returns a {@link JsonSerializer} for a {@link DeleteAppInstance.Response}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(DeleteAppInstance.Response.class, // + json -> new DeleteAppInstance.Response(// + json.getList("warnings", JsonElementPath::getAsString)), // + obj -> JsonUtils.buildJsonObject() // + .add("warnings", obj.warnings().stream() // + .map(JsonPrimitive::new) // + .collect(toJsonArray())) // + .build()); } - @Override - public JsonObject getResult() { - return JsonUtils.buildJsonObject() // - .add("warnings", this.warnings) // - .build(); - } } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetApp.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetApp.java index 4dfed6389ff..1263f5a5ed7 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetApp.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetApp.java @@ -1,8 +1,9 @@ package io.openems.edge.core.appmanager.jsonrpc; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; + import java.util.Arrays; import java.util.List; -import java.util.UUID; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -12,13 +13,15 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.session.Language; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.EndpointRequestType; import io.openems.edge.core.appmanager.OpenemsApp; import io.openems.edge.core.appmanager.OpenemsAppInstance; import io.openems.edge.core.appmanager.flag.Flag; +import io.openems.edge.core.appmanager.jsonrpc.GetApp.Request; +import io.openems.edge.core.appmanager.jsonrpc.GetApp.Response; import io.openems.edge.core.appmanager.validator.OpenemsAppStatus; import io.openems.edge.core.appmanager.validator.Validator; @@ -65,60 +68,77 @@ * } * */ -public class GetApp { - - public static final String METHOD = "getApp"; - - public static class Request extends JsonrpcRequest { +public class GetApp implements EndpointRequestType { - /** - * Parses a generic {@link JsonrpcRequest} to a {@link Request}. - * - * @param r the {@link JsonrpcRequest} - * @return the {@link GetAppsRequest} - * @throws OpenemsNamedException on error - */ - public static Request from(JsonrpcRequest r) throws OpenemsNamedException { - var p = r.getParams(); - var appId = JsonUtils.getAsString(p, "appId"); - return new Request(r, appId); - } + @Override + public String getMethod() { + return "getApp"; + } - public final String appId; + @Override + public JsonSerializer getRequestSerializer() { + return Request.serializer(); + } - private Request(JsonrpcRequest request, String appId) { - super(request, METHOD); - this.appId = appId; - } + @Override + public JsonSerializer getResponseSerializer() { + return Response.serializer(); + } - public Request(String appId) { - super(METHOD); - this.appId = appId; - } + public record Request(// + String appId // + ) { - @Override - public JsonObject getParams() { - return new JsonObject(); + /** + * Returns a {@link JsonSerializer} for a {@link GetApp.Request}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(GetApp.Request.class, // + json -> new GetApp.Request(// + json.getString("appId")), // + obj -> JsonUtils.buildJsonObject() // + .addProperty("appId", obj.appId()) // + .build()); } } - public static class Response extends JsonrpcResponseSuccess { - - private final JsonObject app; + public record Response(// + JsonObject app // + ) { - public Response(UUID id, OpenemsApp app, List instantiatedApps, Language language, + /** + * Creates a Response. + * + * @param app the app of the response + * @param instantiatedApps all created {@link OpenemsAppInstance} + * @param language the current language + * @param validator the {@link Validator} + * @return the created Response + * @throws OpenemsNamedException on error + */ + public static Response newInstance(OpenemsApp app, List instantiatedApps, Language language, Validator validator) throws OpenemsNamedException { - super(id); - this.app = createJsonObjectOf(app, validator, instantiatedApps, language); + return new Response(createJsonObjectOf(app, validator, instantiatedApps, language)); } - @Override - public JsonObject getResult() { - return JsonUtils.buildJsonObject() // - .add("app", this.app) // - .build(); + /** + * Returns a {@link JsonSerializer} for a {@link GetApp.Response}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(GetApp.Response.class, // + json -> new GetApp.Response(JsonUtils.buildJsonObject() // + .addProperty("appId", json.getString("appId")) // + .build()), // + obj -> JsonUtils.buildJsonObject() // + .add("app", obj.app()) // + .build()); } + } /** diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppAssistant.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppAssistant.java index 6532d89fd71..18f99677a19 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppAssistant.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppAssistant.java @@ -1,15 +1,15 @@ package io.openems.edge.core.appmanager.jsonrpc; -import java.util.UUID; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonSerializer; -import com.google.gson.JsonObject; - -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.EndpointRequestType; import io.openems.edge.core.appmanager.AppAssistant; import io.openems.edge.core.appmanager.OpenemsApp; +import io.openems.edge.core.appmanager.jsonrpc.GetAppAssistant.Request; +import io.openems.edge.core.appmanager.jsonrpc.GetAppAssistant.Response; /** * Gets the App-Assistant for a {@link OpenemsApp}. @@ -36,63 +36,61 @@ * "jsonrpc": "2.0", * "id": "UUID", * "result": { - * ... {@link AppAssistant#toJsonObject()} + * ... {@link AppAssistant#serializer()} * } * } * */ -public class GetAppAssistant { +public class GetAppAssistant implements EndpointRequestType { + + @Override + public String getMethod() { + return "getAppAssistant"; + } - public static final String METHOD = "getAppAssistant"; + @Override + public JsonSerializer getRequestSerializer() { + return Request.serializer(); + } - public static class Request extends JsonrpcRequest { + @Override + public JsonSerializer getResponseSerializer() { + return Response.serializer(); + } + public record Request(// + String appId // + ) { /** - * Parses a generic {@link JsonrpcRequest} to a {@link GetAppAssistantRequest}. - * - * @param r the {@link JsonrpcRequest} - * @return the {@link GetAppAssistantRequest} - * @throws OpenemsNamedException on error + * Returns a {@link JsonSerializer} for a {@link GetAppAssistant.Request}. + * + * @return the created {@link JsonSerializer} */ - public static Request from(JsonrpcRequest r) throws OpenemsNamedException { - var p = r.getParams(); - var appId = JsonUtils.getAsString(p, "appId"); - return new Request(r, appId); - } - - public final String appId; - - public Request(JsonrpcRequest request, String appId) { - super(request, METHOD); - this.appId = appId; + public static JsonSerializer serializer() { + return jsonObjectSerializer(GetAppAssistant.Request.class, // + json -> new GetAppAssistant.Request(// + json.getString("appId")), + obj -> JsonUtils.buildJsonObject() // + .addProperty("appId", obj.appId()) // + .build()); } - public Request(String appId) { - super(METHOD); - this.appId = appId; - } - - @Override - public JsonObject getParams() { - return JsonUtils.buildJsonObject() // - .addProperty("appId", this.appId) // - .build(); - } } - public static class Response extends JsonrpcResponseSuccess { - - private final AppAssistant appAssistant; - - public Response(UUID id, AppAssistant appAssistant) { - super(id); - this.appAssistant = appAssistant; + public record Response(// + AppAssistant appAssistant // + ) { + /** + * Returns a {@link JsonSerializer} for a {@link GetAppAssistant.Response}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonSerializer(GetAppAssistant.Response.class, // + json -> new GetAppAssistant.Response(json.getAsObject(AppAssistant.serializer())), // + obj -> AppAssistant.serializer().serialize(obj.appAssistant())); } - @Override - public JsonObject getResult() { - return this.appAssistant.toJsonObject(); - } } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppDescriptor.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppDescriptor.java index 4c5a513bb71..97922ed1d28 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppDescriptor.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppDescriptor.java @@ -1,15 +1,15 @@ package io.openems.edge.core.appmanager.jsonrpc; -import java.util.UUID; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonSerializer; -import com.google.gson.JsonObject; - -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.EndpointRequestType; import io.openems.edge.core.appmanager.AppDescriptor; import io.openems.edge.core.appmanager.OpenemsApp; +import io.openems.edge.core.appmanager.jsonrpc.GetAppDescriptor.Request; +import io.openems.edge.core.appmanager.jsonrpc.GetAppDescriptor.Response; /** * Gets the App-Descriptor for a {@link OpenemsApp}. @@ -36,63 +36,61 @@ * "jsonrpc": "2.0", * "id": "UUID", * "result": { - * ... {@link AppDescriptor#toJsonObject()} + * ... {@link AppDescriptor#serializer()} * } * } * */ -public class GetAppDescriptor { +public class GetAppDescriptor implements EndpointRequestType { + + @Override + public String getMethod() { + return "getAppDescriptor"; + } - public static final String METHOD = "getAppDescriptor"; + @Override + public JsonSerializer getRequestSerializer() { + return Request.serializer(); + } - public static class Request extends JsonrpcRequest { + @Override + public JsonSerializer getResponseSerializer() { + return Response.serializer(); + } + public record Request(// + String appId // + ) { /** - * Parses a generic {@link JsonrpcRequest} to a {@link GetAppAssistantRequest}. - * - * @param r the {@link JsonrpcRequest} - * @return the {@link GetAppAssistantRequest} - * @throws OpenemsNamedException on error + * Returns a {@link JsonSerializer} for a {@link GetAppDescriptor.Request}. + * + * @return the created {@link JsonSerializer} */ - public static Request from(JsonrpcRequest r) throws OpenemsNamedException { - var p = r.getParams(); - var appId = JsonUtils.getAsString(p, "appId"); - return new Request(r, appId); - } - - public final String appId; - - public Request(JsonrpcRequest request, String appId) { - super(request, METHOD); - this.appId = appId; + public static JsonSerializer serializer() { + return jsonObjectSerializer(GetAppDescriptor.Request.class, // + json -> new GetAppDescriptor.Request(// + json.getString("appId")), // + obj -> JsonUtils.buildJsonObject() // + .addProperty("appId", obj.appId()) // + .build()); } - public Request(String appId) { - super(METHOD); - this.appId = appId; - } - - @Override - public JsonObject getParams() { - return JsonUtils.buildJsonObject() // - .addProperty("appId", this.appId) // - .build(); - } } - public static class Response extends JsonrpcResponseSuccess { - - private final AppDescriptor appDescriptor; - - public Response(UUID id, AppDescriptor appDescriptor) { - super(id); - this.appDescriptor = appDescriptor; + public record Response(// + AppDescriptor appDescriptor // + ) { + /** + * Returns a {@link JsonSerializer} for a {@link GetAppDescriptor.Response}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonSerializer(GetAppDescriptor.Response.class, // + json -> new GetAppDescriptor.Response(json.getAsObject(AppDescriptor.serializer())), // + obj -> AppDescriptor.serializer().serialize(obj.appDescriptor())); } - @Override - public JsonObject getResult() { - return this.appDescriptor.toJsonObject(); - } } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppInstances.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppInstances.java index dea459eb9e9..1163db1bab0 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppInstances.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetAppInstances.java @@ -1,17 +1,17 @@ package io.openems.edge.core.appmanager.jsonrpc; -import java.util.UUID; -import java.util.stream.Stream; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; +import static io.openems.common.utils.JsonUtils.toJsonArray; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; +import java.util.List; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.EndpointRequestType; import io.openems.edge.core.appmanager.OpenemsApp; import io.openems.edge.core.appmanager.OpenemsAppInstance; +import io.openems.edge.core.appmanager.jsonrpc.GetAppInstances.Request; +import io.openems.edge.core.appmanager.jsonrpc.GetAppInstances.Response; /** * Gets the active instances of an {@link OpenemsApp}. @@ -43,61 +43,59 @@ * } * */ -public class GetAppInstances { +public class GetAppInstances implements EndpointRequestType { - public static final String METHOD = "getAppInstances"; + @Override + public String getMethod() { + return "getAppInstances"; + } + + @Override + public JsonSerializer getRequestSerializer() { + return Request.serializer(); + } - public static class Request extends JsonrpcRequest { + @Override + public JsonSerializer getResponseSerializer() { + return Response.serializer(); + } + public record Request(// + String appId // + ) { /** - * Parses a generic {@link JsonrpcRequest} to a {@link GetAppInstances}. - * - * @param r the {@link JsonrpcRequest} - * @return the {@link GetAppInstances} - * @throws OpenemsNamedException on error + * Returns a {@link JsonSerializer} for a {@link GetAppInstances.Request}. + * + * @return the created {@link JsonSerializer} */ - public static Request from(JsonrpcRequest r) throws OpenemsNamedException { - var p = r.getParams(); - var appId = JsonUtils.getAsString(p, "appId"); - return new Request(r, appId); - } - - public final String appId; - - private Request(JsonrpcRequest request, String appId) { - super(request, METHOD); - this.appId = appId; - } - - public Request(String appId) { - super(METHOD); - this.appId = appId; + public static JsonSerializer serializer() { + return jsonObjectSerializer(GetAppInstances.Request.class, // + json -> new GetAppInstances.Request(// + json.getString("appId")), // + obj -> JsonUtils.buildJsonObject() // + .addProperty("appId", obj.appId()) // + .build()); } - @Override - public JsonObject getParams() { - return JsonUtils.buildJsonObject() // - .addProperty("appId", this.appId) // - .build(); - } } - public static class Response extends JsonrpcResponseSuccess { - - private final JsonArray instances; - - public Response(UUID id, Stream instances) { - super(id); + public record Response(// + List instances // + ) { - this.instances = instances.map(OpenemsAppInstance::toJsonObject) // - .collect(JsonUtils.toJsonArray()); - } - - @Override - public JsonObject getResult() { - return JsonUtils.buildJsonObject() // - .add("instances", this.instances) // - .build(); // + /** + * Returns a {@link JsonSerializer} for a {@link GetAppInstances.Response}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(GetAppInstances.Response.class, // + json -> new GetAppInstances.Response(json.getList("instances", OpenemsAppInstance.serializer())), // + obj -> JsonUtils.buildJsonObject() // + .add("instances", obj.instances().stream() // + .map(OpenemsAppInstance.serializer()::serialize) // + .collect(toJsonArray())) // + .build()); } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetApps.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetApps.java index e5ee12319e4..b25eacfc46f 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetApps.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/GetApps.java @@ -1,21 +1,23 @@ package io.openems.edge.core.appmanager.jsonrpc; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.emptyObjectSerializer; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; + import java.util.List; import java.util.Objects; -import java.util.UUID; import com.google.gson.JsonArray; -import com.google.gson.JsonObject; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.session.Language; import io.openems.common.session.Role; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.EndpointRequestType; import io.openems.edge.core.appmanager.OpenemsApp; import io.openems.edge.core.appmanager.OpenemsAppInstance; +import io.openems.edge.core.appmanager.jsonrpc.GetApps.Request; +import io.openems.edge.core.appmanager.jsonrpc.GetApps.Response; import io.openems.edge.core.appmanager.validator.Validator; /** @@ -61,39 +63,70 @@ * } * */ -public class GetApps { +public class GetApps implements EndpointRequestType { + + @Override + public String getMethod() { + return "getApps"; + } - public static final String METHOD = "getApps"; + @Override + public JsonSerializer getRequestSerializer() { + return Request.serializer(); + } + + @Override + public JsonSerializer getResponseSerializer() { + return Response.serializer(); + } - public static class Request extends JsonrpcRequest { + public record Request() { /** - * Parses a generic {@link JsonrpcRequest} to a {@link Request}. - * - * @param r the {@link JsonrpcRequest} - * @return the {@link GetAppsRequest} - * @throws OpenemsNamedException on error + * Returns a {@link JsonSerializer} for a {@link GetApps.Response}. + * + * @return the created {@link JsonSerializer} */ - public static Request from(JsonrpcRequest r) throws OpenemsException { - return new Request(r); + public static JsonSerializer serializer() { + return emptyObjectSerializer(Request::new); } - public Request() { - super(METHOD); - } + } - private Request(JsonrpcRequest request) { - super(request, METHOD); - } + public record Response(// + JsonArray apps // + ) { - @Override - public JsonObject getParams() { - return new JsonObject(); + /** + * Creates a new Response. + * + * @param availableApps all available app + * @param instantiatedApps all {@link OpenemsAppInstance} + * @param userRole the current {@link Role} of the user + * @param language the current {@link Language} of the user + * @param validator the {@link Validator} to validate the app + * @return the created Response + */ + public static Response newInstance(List availableApps, List instantiatedApps, + Role userRole, Language language, Validator validator) { + return new Response(createAppsArray(availableApps, instantiatedApps, userRole, language, validator)); } - } - - public static class Response extends JsonrpcResponseSuccess { + /** + * Returns a {@link JsonSerializer} for a {@link GetApps.Response}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(Response.class, json -> { + // TODO serialize whole apps not only JsonArray + return new Response(json.getJsonArray("apps")); + }, obj -> { + return JsonUtils.buildJsonObject() // + .add("apps", obj.apps()) // + .build(); + }); + } private static JsonArray createAppsArray(List availableApps, List instantiatedApps, Role userRole, Language language, Validator validator) { @@ -118,20 +151,6 @@ private static JsonArray createAppsArray(List availableApps, .collect(JsonUtils.toJsonArray()); } - private final JsonArray apps; - - public Response(UUID id, List availableApps, List instantiatedApps, - Role userRole, Language language, Validator validator) { - super(id); - this.apps = createAppsArray(availableApps, instantiatedApps, userRole, language, validator); - } - - @Override - public JsonObject getResult() { - return JsonUtils.buildJsonObject() // - .add("apps", this.apps) // - .build(); - } } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/UpdateAppInstance.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/UpdateAppInstance.java index a297b0c6b52..50facf512a4 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/UpdateAppInstance.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/jsonrpc/UpdateAppInstance.java @@ -1,17 +1,21 @@ package io.openems.edge.core.appmanager.jsonrpc; +import static io.openems.common.jsonrpc.serialization.JsonSerializerUtil.jsonObjectSerializer; +import static io.openems.common.utils.JsonUtils.toJsonArray; + import java.util.List; import java.util.UUID; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; -import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.jsonrpc.serialization.JsonElementPath; +import io.openems.common.jsonrpc.serialization.JsonSerializer; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.jsonapi.EndpointRequestType; import io.openems.edge.core.appmanager.OpenemsAppInstance; +import io.openems.edge.core.appmanager.jsonrpc.UpdateAppInstance.Request; +import io.openems.edge.core.appmanager.jsonrpc.UpdateAppInstance.Response; /** * Updates an {@link OpenemsAppInstance}.. @@ -45,74 +49,72 @@ * } * */ -public class UpdateAppInstance { - - public static final String METHOD = "updateAppInstance"; +public class UpdateAppInstance implements EndpointRequestType { - public static class Request extends JsonrpcRequest { + @Override + public String getMethod() { + return "updateAppInstance"; + } - /** - * Parses a generic {@link JsonrpcRequest} to a {@link UpdateAppInstance}. - * - * @param r the {@link JsonrpcRequest} - * @return the {@link UpdateAppInstance} - * @throws OpenemsNamedException on error - */ - public static Request from(JsonrpcRequest r) throws OpenemsNamedException { - var p = r.getParams(); - var instanceId = JsonUtils.getAsUUID(p, "instanceId"); - var alias = JsonUtils.getAsString(p, "alias"); - var properties = JsonUtils.getAsJsonObject(p, "properties"); - return new Request(r, instanceId, alias, properties); - } + @Override + public JsonSerializer getRequestSerializer() { + return Request.serializer(); + } - public final UUID instanceId; - public final String alias; - public final JsonObject properties; + @Override + public JsonSerializer getResponseSerializer() { + return Response.serializer(); + } - private Request(JsonrpcRequest request, UUID instanceId, String alias, JsonObject properties) { - super(request, METHOD); - this.instanceId = instanceId; - this.alias = alias; - this.properties = properties; - } + public record Request(// + UUID instanceId, // + String alias, // + JsonObject properties // + ) { - public Request(UUID instanceId, String alias, JsonObject properties) { - super(METHOD); - this.instanceId = instanceId; - this.alias = alias; - this.properties = properties; + /** + * Returns a {@link JsonSerializer} for a {@link UpdateAppInstance.Request}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(UpdateAppInstance.Request.class, // + json -> new UpdateAppInstance.Request(// + json.getStringPath("instanceId").getAsUuid(), // + json.getString("alias"), // + json.getJsonObject("properties")), // + obj -> JsonUtils.buildJsonObject() // + .addProperty("instanceId", obj.instanceId().toString()) // + .addProperty("alias", obj.alias()) // + .add("properties", obj.properties()) // + .build()); } - @Override - public JsonObject getParams() { - return JsonUtils.buildJsonObject() // - .addProperty("instanceId", this.instanceId.toString()) // - .addProperty("alias", this.alias) // - .add("properties", this.properties) // - .build(); - } } - public static class Response extends JsonrpcResponseSuccess { + public record Response(// + OpenemsAppInstance instance, // + List warnings // + ) { - public final OpenemsAppInstance instance; - public final JsonArray warnings; - - public Response(UUID id, OpenemsAppInstance instance, List warnings) { - super(id); - this.instance = instance; - this.warnings = warnings == null ? new JsonArray() - : warnings.stream().map(JsonPrimitive::new).collect(JsonUtils.toJsonArray()); + /** + * Returns a {@link JsonSerializer} for a {@link UpdateAppInstance.Response}. + * + * @return the created {@link JsonSerializer} + */ + public static JsonSerializer serializer() { + return jsonObjectSerializer(UpdateAppInstance.Response.class, // + json -> new UpdateAppInstance.Response(// + json.getElement("instance", OpenemsAppInstance.serializer()), // + json.getList("warnings", JsonElementPath::getAsString)), // + obj -> JsonUtils.buildJsonObject() // + .add("instance", OpenemsAppInstance.serializer().serialize(obj.instance())) // + .add("warnings", obj.warnings().stream() // + .map(JsonPrimitive::new) // + .collect(toJsonArray())) // + .build()); } - @Override - public JsonObject getResult() { - return JsonUtils.buildJsonObject() // - .add("instance", this.instance.toJsonObject()) // - .add("warnings", this.warnings) // - .build(); - } } } diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/translation_de.properties b/io.openems.edge.core/src/io/openems/edge/core/appmanager/translation_de.properties index 43f56722dfc..d0e0f07245e 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/translation_de.properties +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/translation_de.properties @@ -214,9 +214,12 @@ App.IntegratedSystem.predictor0.alias = Prognose App.IntegratedSystem.ctrlEssSurplusFeedToGrid0.alias = Überschusseinspeisung App.IntegratedSystem.emergencyMeter.alias = Notstromverbraucher App.IntegratedSystem.ctrlEmergencyCapacityReserve0.alias = Ansteuerung der Notstromreserve -App.IntegratedSystem.hasPv.label = Hat DC-PV {0} (MPPT {1}) +App.IntegratedSystem.hasPv.label = Hat DC-PV {0} (MPPT {1}) (Nicht mehr unterstützt) +App.IntegratedSystem.hasMppt.label = Hat MPPT {0} App.IntegratedSystem.pvAlias.label = PV {0} Alias App.IntegratedSystem.pvAlias.alias = PV {0} +App.IntegratedSystem.mpptAlias.label = MPPT {0} Alias +App.IntegratedSystem.mpptAlias.alias = MPPT {0} App.IntegratedSystem.hasAcMeter.label = Hat AC-Zähler App.IntegratedSystem.acMeterType.label = AC-Zähler Typ diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/translation_en.properties b/io.openems.edge.core/src/io/openems/edge/core/appmanager/translation_en.properties index 53bbdfccfc7..75b69cbbf27 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/translation_en.properties +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/translation_en.properties @@ -214,9 +214,12 @@ App.IntegratedSystem.predictor0.alias = Forecast App.IntegratedSystem.ctrlEssSurplusFeedToGrid0.alias = Excess feed-in App.IntegratedSystem.emergencyMeter.alias = Emergency power consumers App.IntegratedSystem.ctrlEmergencyCapacityReserve0.alias = Control of the emergency power reserve -App.IntegratedSystem.hasPv.label = Has DC-PV {0} (MPPT {1}) +App.IntegratedSystem.hasPv.label = Has DC-PV {0} (MPPT {1}) (No longer supported) +App.IntegratedSystem.hasMppt.label = Hat MPPT {0} App.IntegratedSystem.pvAlias.label = PV {0} Alias App.IntegratedSystem.pvAlias.alias = PV {0} +App.IntegratedSystem.mpptAlias.label = MPPT {0} Alias +App.IntegratedSystem.mpptAlias.alias = MPPT {0} App.IntegratedSystem.hasAcMeter.label = Has AC meter App.IntegratedSystem.acMeterType.label = AC-Meter Type diff --git a/io.openems.edge.core/src/io/openems/edge/core/appmanager/validator/Checkables.java b/io.openems.edge.core/src/io/openems/edge/core/appmanager/validator/Checkables.java index cbd23d84ab7..65b03fa36c4 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/appmanager/validator/Checkables.java +++ b/io.openems.edge.core/src/io/openems/edge/core/appmanager/validator/Checkables.java @@ -41,7 +41,7 @@ public static CheckableConfig checkRelayCount(String io, int count, InjectableCo * Creates a {@link CheckableConfig} which checks if any installed relay has at * least the given amount of ports available. * - * @param count the number of available ports + * @param count the number of available ports * @param filters additional relay filter * @return the {@link CheckableConfig} */ diff --git a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java index 79245de3195..1df00beb666 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/ComponentManagerImpl.java @@ -9,7 +9,6 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; -import java.util.concurrent.CompletableFuture; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; @@ -37,8 +36,6 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; import io.openems.common.jsonrpc.request.GetEdgeConfigRequest; @@ -52,7 +49,10 @@ import io.openems.edge.common.component.ClockProvider; import io.openems.edge.common.component.ComponentManager; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.EdgeGuards; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApiBuilder; import io.openems.edge.common.user.User; import io.openems.edge.core.componentmanager.jsonrpc.ChannelExportXlsxRequest; import io.openems.edge.core.componentmanager.jsonrpc.ChannelExportXlsxResponse; @@ -65,7 +65,7 @@ "enabled=true" // }) public class ComponentManagerImpl extends AbstractOpenemsComponent - implements ComponentManager, OpenemsComponent, JsonApi, ConfigurationListener { + implements ComponentManager, OpenemsComponent, ConfigurationListener, ComponentJsonApi { private final List workers = new ArrayList<>(); private final EdgeConfigWorker edgeConfigWorker; @@ -215,6 +215,9 @@ private List getComponentsViaService(Class clazz, String filter) { // filter invalid e.printStackTrace(); return Collections.emptyList(); + } catch (RuntimeException e) { + e.printStackTrace(); + return Collections.emptyList(); } } @@ -282,30 +285,64 @@ protected void logError(Logger log, String message) { } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException { - user.assertRoleIsAtLeast("handleJsonrpcRequest", Role.GUEST); - - switch (request.getMethod()) { - - case GetEdgeConfigRequest.METHOD: - return this.handleGetEdgeConfigRequest(user, GetEdgeConfigRequest.from(request)); - - case CreateComponentConfigRequest.METHOD: - return this.handleCreateComponentConfigRequest(user, CreateComponentConfigRequest.from(request)); - - case UpdateComponentConfigRequest.METHOD: - return this.handleUpdateComponentConfigRequest(user, UpdateComponentConfigRequest.from(request)); - - case DeleteComponentConfigRequest.METHOD: - return this.handleDeleteComponentConfigRequest(user, DeleteComponentConfigRequest.from(request)); - - case ChannelExportXlsxRequest.METHOD: - return this.handleChannelExportXlsxRequest(user, ChannelExportXlsxRequest.from(request)); - - default: - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(GetEdgeConfigRequest.METHOD, endpoint -> { + endpoint.setDescription(""" + Handles a GetEdgeConfigRequest. + """) // + .setGuards(EdgeGuards.roleIsAtleast(Role.GUEST)); + }, t -> { + return this.handleGetEdgeConfigRequest(t.get(EdgeKeys.USER_KEY), // + GetEdgeConfigRequest.from(t.getRequest())); + }); + + builder.handleRequest(CreateComponentConfigRequest.METHOD, endpoint -> { + endpoint.setDescription(""" + Handles a CreateComponentConfigRequest. + """) // + .setGuards(EdgeGuards.roleIsAtleastFromBackend(Role.INSTALLER), // + EdgeGuards.roleIsAtleastNotFromBackend(Role.ADMIN)); + }, t -> { + this.handleCreateComponentConfigRequest(t.get(EdgeKeys.USER_KEY), // + CreateComponentConfigRequest.from(t.getRequest())); + + return new GenericJsonrpcResponseSuccess(t.getRequest().getId()); + }); + + builder.handleRequest(UpdateComponentConfigRequest.METHOD, endpoint -> { + endpoint.setDescription(""" + Handles a UpdateComponentConfigRequest. + """) // + .setGuards(EdgeGuards.roleIsAtleast(Role.OWNER)); + }, t -> { + this.handleUpdateComponentConfigRequest(t.get(EdgeKeys.USER_KEY), // + UpdateComponentConfigRequest.from(t.getRequest())); + + return new GenericJsonrpcResponseSuccess(t.getRequest().getId()); + }); + + builder.handleRequest(DeleteComponentConfigRequest.METHOD, endpoint -> { + endpoint.setDescription(""" + Handles a DeleteComponentConfigRequest. + """) // + .setGuards(EdgeGuards.roleIsAtleastFromBackend(Role.INSTALLER), // + EdgeGuards.roleIsAtleastNotFromBackend(Role.ADMIN)); + }, t -> { + this.handleDeleteComponentConfigRequest(t.get(EdgeKeys.USER_KEY), // + DeleteComponentConfigRequest.from(t.getRequest())); + + return new GenericJsonrpcResponseSuccess(t.getRequest().getId()); + }); + + builder.handleRequest(ChannelExportXlsxRequest.METHOD, endpoint -> { + endpoint.setDescription(""" + Handles a ChannelExportXlsxRequest. + """) // + .setGuards(EdgeGuards.roleIsAtleast(Role.ADMIN)); + }, t -> { + return this.handleChannelExportXlsxRequest(t.get(EdgeKeys.USER_KEY), // + ChannelExportXlsxRequest.from(t.getRequest())); + }); } /** @@ -316,23 +353,15 @@ public CompletableFuture handleJsonrpcRequest(User user, * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleGetEdgeConfigRequest(User user, - GetEdgeConfigRequest request) throws OpenemsNamedException { + private GetEdgeConfigResponse handleGetEdgeConfigRequest(User user, GetEdgeConfigRequest request) + throws OpenemsNamedException { var config = this.getEdgeConfig(); - var response = new GetEdgeConfigResponse(request.getId(), config); - return CompletableFuture.completedFuture(response); + return new GetEdgeConfigResponse(request.getId(), config); } - /** - * Handles a {@link CreateComponentConfigRequest}. - * - * @param user the {@link User} - * @param request the {@link CreateComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - public CompletableFuture handleCreateComponentConfigRequest(User user, - CreateComponentConfigRequest request) throws OpenemsNamedException { + @Override + public void handleCreateComponentConfigRequest(User user, CreateComponentConfigRequest request) + throws OpenemsNamedException { // Get Component-ID from Request String componentId = null; for (Property property : request.getProperties()) { @@ -395,20 +424,11 @@ public CompletableFuture handleCreateComponentConfigRequ e.printStackTrace(); throw OpenemsError.EDGE_UNABLE_TO_CREATE_CONFIG.exception(request.getFactoryPid(), e.getMessage()); } - - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); } - /** - * Handles a {@link UpdateComponentConfigRequest}. - * - * @param user the {@link User} - * @param request the {@link UpdateComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - public CompletableFuture handleUpdateComponentConfigRequest(User user, - UpdateComponentConfigRequest request) throws OpenemsNamedException { + @Override + public void handleUpdateComponentConfigRequest(User user, UpdateComponentConfigRequest request) + throws OpenemsNamedException { var config = this.getExistingConfigForId(request.getComponentId()); // Create map with changed configuration attributes @@ -451,20 +471,11 @@ public CompletableFuture handleUpdateComponentConfigRequ e.printStackTrace(); throw OpenemsError.EDGE_UNABLE_TO_APPLY_CONFIG.exception(request.getComponentId(), e.getMessage()); } - - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); } - /** - * Handles a {@link DeleteComponentConfigRequest}. - * - * @param user the {@link User} - * @param request the {@link DeleteComponentConfigRequest} - * @return the Future JSON-RPC Response - * @throws OpenemsNamedException on error - */ - public CompletableFuture handleDeleteComponentConfigRequest(User user, - DeleteComponentConfigRequest request) throws OpenemsNamedException { + @Override + public void handleDeleteComponentConfigRequest(User user, DeleteComponentConfigRequest request) + throws OpenemsNamedException { var config = this.getExistingConfigForId(request.getComponentId()); try { @@ -473,8 +484,6 @@ public CompletableFuture handleDeleteComponentConfigRequ e.printStackTrace(); throw OpenemsError.EDGE_UNABLE_TO_DELETE_CONFIG.exception(request.getComponentId(), e.getMessage()); } - - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); } /** @@ -485,14 +494,13 @@ public CompletableFuture handleDeleteComponentConfigRequ * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - protected CompletableFuture handleChannelExportXlsxRequest(User user, - ChannelExportXlsxRequest request) throws OpenemsNamedException { - user.assertRoleIsAtLeast("ChannelExportXlsxRequest", Role.ADMIN); + protected ChannelExportXlsxResponse handleChannelExportXlsxRequest(User user, ChannelExportXlsxRequest request) + throws OpenemsNamedException { var component = this.getComponent(request.getComponentId()); if (component == null) { throw OpenemsError.EDGE_NO_COMPONENT_WITH_ID.exception(request.getComponentId()); } - return CompletableFuture.completedFuture(new ChannelExportXlsxResponse(request.getId(), component)); + return new ChannelExportXlsxResponse(request.getId(), component); } /** diff --git a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/DefaultConfigurationWorker.java b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/DefaultConfigurationWorker.java index 3131bc5fc76..c884719aea5 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/componentmanager/DefaultConfigurationWorker.java +++ b/io.openems.edge.core/src/io/openems/edge/core/componentmanager/DefaultConfigurationWorker.java @@ -6,9 +6,6 @@ import java.util.Hashtable; import java.util.List; import java.util.Optional; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -180,11 +177,9 @@ protected void createConfiguration(AtomicBoolean defaultConfigurationFailed, Str "Creating Component configuration [" + factoryPid + "]: " + properties.stream() // .map(p -> p.getName() + ":" + p.getValue().toString()) // .collect(Collectors.joining(", "))); - var response = this.parent.handleCreateComponentConfigRequest(null /* no user */, + this.parent.handleCreateComponentConfigRequest(null /* no user */, new CreateComponentConfigRequest(factoryPid, properties)); - response.get(60, TimeUnit.SECONDS); - - } catch (OpenemsNamedException | InterruptedException | ExecutionException | TimeoutException e) { + } catch (OpenemsNamedException e) { this.parent.logError(this.log, "Unable to create Component configuration for Factory [" + factoryPid + "]: " + e.getMessage()); e.printStackTrace(); @@ -208,11 +203,9 @@ protected void updateConfiguration(AtomicBoolean defaultConfigurationFailed, Str .map(p -> p.getName() + ":" + p.getValue().toString()) // .collect(Collectors.joining(", "))); - var response = this.parent.handleUpdateComponentConfigRequest(null /* no user */, + this.parent.handleUpdateComponentConfigRequest(null /* no user */, new UpdateComponentConfigRequest(componentId, properties)); - response.get(60, TimeUnit.SECONDS); - - } catch (OpenemsNamedException | InterruptedException | ExecutionException | TimeoutException e) { + } catch (OpenemsNamedException e) { this.parent.logError(this.log, "Unable to update Component configuration for Component [" + componentId + "]: " + e.getMessage()); e.printStackTrace(); @@ -231,11 +224,9 @@ protected void deleteConfiguration(AtomicBoolean defaultConfigurationFailed, Str try { this.parent.logInfo(this.log, "Deleting Component [" + componentId + "]"); - var response = this.parent.handleDeleteComponentConfigRequest(null /* no user */, + this.parent.handleDeleteComponentConfigRequest(null /* no user */, new DeleteComponentConfigRequest(componentId)); - response.get(60, TimeUnit.SECONDS); - - } catch (OpenemsNamedException | InterruptedException | ExecutionException | TimeoutException e) { + } catch (OpenemsNamedException e) { this.parent.logError(this.log, "Unable to delete Component [" + componentId + "]: " + e.getMessage()); e.printStackTrace(); defaultConfigurationFailed.set(true); diff --git a/io.openems.edge.core/src/io/openems/edge/core/host/HostImpl.java b/io.openems.edge.core/src/io/openems/edge/core/host/HostImpl.java index fa89606796f..bc18b13fab4 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/host/HostImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/host/HostImpl.java @@ -17,18 +17,18 @@ import org.osgi.service.metatype.annotations.Designate; import org.slf4j.Logger; -import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; -import io.openems.common.jsonrpc.base.JsonrpcRequest; import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.oem.OpenemsEdgeOem; import io.openems.common.session.Role; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.host.Host; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApiBuilder; import io.openems.edge.common.user.User; import io.openems.edge.core.host.jsonrpc.ExecuteSystemCommandRequest; import io.openems.edge.core.host.jsonrpc.ExecuteSystemRestartRequest; @@ -48,7 +48,7 @@ property = { // "enabled=true" // }) -public class HostImpl extends AbstractOpenemsComponent implements Host, OpenemsComponent, JsonApi { +public class HostImpl extends AbstractOpenemsComponent implements Host, OpenemsComponent, ComponentJsonApi { protected final OperatingSystem operatingSystem; @@ -140,32 +140,36 @@ protected void deactivate() { } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException { - user.assertRoleIsAtLeast("handleJsonrpcRequest", Role.OWNER); - switch (request.getMethod()) { - - case GetNetworkConfigRequest.METHOD: - return this.handleGetNetworkConfigRequest(user, GetNetworkConfigRequest.from(request)); - - case SetNetworkConfigRequest.METHOD: - return this.handleSetNetworkConfigRequest(user, SetNetworkConfigRequest.from(request)); - - case GetSystemUpdateStateRequest.METHOD: - return this.handleGetSystemUpdateStateRequest(user, GetSystemUpdateStateRequest.from(request)); - - case ExecuteSystemUpdateRequest.METHOD: - return this.handleExecuteSystemUpdateRequest(user, ExecuteSystemUpdateRequest.from(request)); - - case ExecuteSystemCommandRequest.METHOD: - return this.handleExecuteCommandRequest(user, ExecuteSystemCommandRequest.from(request)); - - case ExecuteSystemRestartRequest.METHOD: - return this.handleExecuteSystemRestartRequest(user, ExecuteSystemRestartRequest.from(request)); - - default: - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(GetNetworkConfigRequest.METHOD, call -> { + return this.handleGetNetworkConfigRequest(call.get(EdgeKeys.USER_KEY), + GetNetworkConfigRequest.from(call.getRequest())); + }); + + builder.handleRequest(SetNetworkConfigRequest.METHOD, call -> { + return this.handleSetNetworkConfigRequest(call.get(EdgeKeys.USER_KEY), + SetNetworkConfigRequest.from(call.getRequest())); + }); + + builder.handleRequest(GetSystemUpdateStateRequest.METHOD, call -> { + return this.handleGetSystemUpdateStateRequest(call.get(EdgeKeys.USER_KEY), + GetSystemUpdateStateRequest.from(call.getRequest())).get(); + }); + + builder.handleRequest(ExecuteSystemUpdateRequest.METHOD, call -> { + return this.handleExecuteSystemUpdateRequest(call.get(EdgeKeys.USER_KEY), + ExecuteSystemUpdateRequest.from(call.getRequest())).get(); + }); + + builder.handleRequest(ExecuteSystemCommandRequest.METHOD, call -> { + return this.handleExecuteCommandRequest(call.get(EdgeKeys.USER_KEY), + ExecuteSystemCommandRequest.from(call.getRequest())).get(); + }); + + builder.handleRequest(ExecuteSystemRestartRequest.METHOD, call -> { + return this.handleExecuteSystemRestartRequest(call.get(EdgeKeys.USER_KEY), + ExecuteSystemRestartRequest.from(call.getRequest())).get(); + }); } /** @@ -176,12 +180,11 @@ public CompletableFuture handleJsonrpcRequest( * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleGetNetworkConfigRequest(User user, - GetNetworkConfigRequest request) throws OpenemsNamedException { + private GetNetworkConfigResponse handleGetNetworkConfigRequest(User user, GetNetworkConfigRequest request) + throws OpenemsNamedException { user.assertRoleIsAtLeast("handleGetNetworkConfigRequest", Role.OWNER); var config = this.operatingSystem.getNetworkConfiguration(); - var response = new GetNetworkConfigResponse(request.getId(), config); - return CompletableFuture.completedFuture(response); + return new GetNetworkConfigResponse(request.getId(), config); } /** @@ -192,8 +195,8 @@ private CompletableFuture handleGetNetworkConfigRequest( * @return the Future JSON-RPC Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleSetNetworkConfigRequest(User user, - SetNetworkConfigRequest request) throws OpenemsNamedException { + public GenericJsonrpcResponseSuccess handleSetNetworkConfigRequest(User user, SetNetworkConfigRequest request) + throws OpenemsNamedException { user.assertRoleIsAtLeast("handleSetNetworkConfigRequest", Role.OWNER); var oldNetworkConfiguration = this.operatingSystem.getNetworkConfiguration(); this.operatingSystem.handleSetNetworkConfigRequest(user, oldNetworkConfiguration, request); @@ -201,7 +204,7 @@ private CompletableFuture handleSetNetworkConfigRequest( // Notify NetworkConfigurationWorker about the change this.networkConfigurationWorker.triggerNextRun(); - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); + return new GenericJsonrpcResponseSuccess(request.getId()); } /** diff --git a/io.openems.edge.core/src/io/openems/edge/core/predictormanager/PredictorManagerImpl.java b/io.openems.edge.core/src/io/openems/edge/core/predictormanager/PredictorManagerImpl.java index c2393659ebf..5e820a73686 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/predictormanager/PredictorManagerImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/predictormanager/PredictorManagerImpl.java @@ -127,7 +127,7 @@ private Prediction getPredictionSum(Sum.ChannelId channelId) { ESS_DISCHARGE_POWER, ESS_MIN_DISCHARGE_POWER, ESS_MAX_DISCHARGE_POWER, ESS_MAX_APPARENT_POWER, ESS_REACTIVE_POWER, ESS_SOC, // - GRID_ACTIVE_POWER, GRID_ACTIVE_POWER_L1, GRID_ACTIVE_POWER_L2, GRID_ACTIVE_POWER_L3, + GRID_ACTIVE_POWER, GRID_ACTIVE_POWER_L1, GRID_ACTIVE_POWER_L2, GRID_ACTIVE_POWER_L3, GRID_BUY_PRICE, GRID_BUY_ACTIVE_ENERGY, GRID_MAX_ACTIVE_POWER, GRID_MIN_ACTIVE_POWER, GRID_MODE, GRID_SELL_ACTIVE_ENERGY, // diff --git a/io.openems.edge.core/src/io/openems/edge/core/sum/SumImpl.java b/io.openems.edge.core/src/io/openems/edge/core/sum/SumImpl.java index 4901c486e17..c7aca7017cc 100644 --- a/io.openems.edge.core/src/io/openems/edge/core/sum/SumImpl.java +++ b/io.openems.edge.core/src/io/openems/edge/core/sum/SumImpl.java @@ -21,6 +21,7 @@ import io.openems.common.channel.AccessMode; import io.openems.common.channel.Level; +import io.openems.edge.common.channel.calculate.CalculateAverage; import io.openems.edge.common.channel.calculate.CalculateIntegerSum; import io.openems.edge.common.channel.calculate.CalculateLongSum; import io.openems.edge.common.component.AbstractOpenemsComponent; @@ -42,6 +43,7 @@ import io.openems.edge.meter.api.ElectricityMeter; import io.openems.edge.meter.api.VirtualMeter; import io.openems.edge.timedata.api.Timedata; +import io.openems.edge.timeofusetariff.api.TimeOfUseTariff; @Designate(ocd = Config.class, factory = false) @Component(// @@ -170,6 +172,7 @@ private void calculateChannelValues() { final var gridActivePowerL1 = new CalculateIntegerSum(); final var gridActivePowerL2 = new CalculateIntegerSum(); final var gridActivePowerL3 = new CalculateIntegerSum(); + final var gridBuyPrice = new CalculateAverage(); final var gridBuyActiveEnergy = new CalculateLongSum(); final var gridSellActiveEnergy = new CalculateLongSum(); @@ -302,6 +305,12 @@ private void calculateChannelValues() { } managedConsumptionActivePower.addValue(evcs.getChargePowerChannel()); + + } else if (component instanceof TimeOfUseTariff tou) { + /* + * Time-of-Use-Tariff + */ + gridBuyPrice.addValue(tou.getPrices().getFirst()); } } @@ -348,6 +357,7 @@ private void calculateChannelValues() { this._setGridActivePowerL2(gridActivePowerL2Sum); var gridActivePowerL3Sum = gridActivePowerL3.calculate(); this._setGridActivePowerL3(gridActivePowerL3Sum); + this._setGridBuyPrice(gridBuyPrice.calculate()); var gridBuyActiveEnergySum = gridBuyActiveEnergy.calculate(); gridBuyActiveEnergySum = this.energyValuesHandler.setValue(Sum.ChannelId.GRID_BUY_ACTIVE_ENERGY, diff --git a/io.openems.edge.core/test/io/openems/edge/app/evcs/TestEvcsCluster.java b/io.openems.edge.core/test/io/openems/edge/app/evcs/TestEvcsCluster.java index c5d1dbd4297..06378d223fc 100644 --- a/io.openems.edge.core/test/io/openems/edge/app/evcs/TestEvcsCluster.java +++ b/io.openems.edge.core/test/io/openems/edge/app/evcs/TestEvcsCluster.java @@ -115,26 +115,26 @@ public void testReinstallationWhenMoreOrEqualOfTwoEvcsExist() throws Exception { assertEquals(2, this.appManagerTestBundle.sut.getInstantiatedApps().size()); - this.appManagerTestBundle.componentManger.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.componentManger.handleCreateComponentConfigRequest(DUMMY_ADMIN, new CreateComponentConfigRequest("Evcs.Keba.KeContact", Lists.newArrayList(// new UpdateComponentConfigRequest.Property("id", "evcs0"), // new UpdateComponentConfigRequest.Property("ip", "1.1.1.1") // - ))).get(); - this.appManagerTestBundle.componentManger.handleJsonrpcRequest(DUMMY_ADMIN, + ))); + this.appManagerTestBundle.componentManger.handleCreateComponentConfigRequest(DUMMY_ADMIN, new CreateComponentConfigRequest("Controller.Evcs", Lists.newArrayList(// new UpdateComponentConfigRequest.Property("id", "ctrlEvcs0"), // new UpdateComponentConfigRequest.Property("evcs.id", "evcs0") // - ))).get(); - this.appManagerTestBundle.componentManger.handleJsonrpcRequest(DUMMY_ADMIN, + ))); + this.appManagerTestBundle.componentManger.handleCreateComponentConfigRequest(DUMMY_ADMIN, new CreateComponentConfigRequest("Evcs.Keba.KeContact", Lists.newArrayList(// new UpdateComponentConfigRequest.Property("id", "evcs1"), // new UpdateComponentConfigRequest.Property("ip", "1.1.1.2") // - ))).get(); - this.appManagerTestBundle.componentManger.handleJsonrpcRequest(DUMMY_ADMIN, + ))); + this.appManagerTestBundle.componentManger.handleCreateComponentConfigRequest(DUMMY_ADMIN, new CreateComponentConfigRequest("Controller.Evcs", Lists.newArrayList(// new UpdateComponentConfigRequest.Property("id", "ctrlEvcs1"), // new UpdateComponentConfigRequest.Property("evcs.id", "evcs1") // - ))).get(); + ))); ResolveDependencies.resolveDependencies(DUMMY_ADMIN, this.appManagerTestBundle.sut, this.appManagerTestBundle.appManagerUtil); @@ -167,7 +167,7 @@ public void testClusterWasAlreadyExisting() throws Exception { this.installKeba("1.1.1.1"); final var clusterId = "evcsCluster0"; - this.appManagerTestBundle.componentManger.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.componentManger.handleCreateComponentConfigRequest(DUMMY_ADMIN, new CreateComponentConfigRequest("Evcs.Cluster.PeakShaving", Lists.newArrayList(// new UpdateComponentConfigRequest.Property("id", clusterId), // new UpdateComponentConfigRequest.Property("enabled", false), // @@ -175,7 +175,7 @@ public void testClusterWasAlreadyExisting() throws Exception { .add("evcs0") // .build()), // new UpdateComponentConfigRequest.Property("hardwarePowerLimitPerPhase", 1234) // - ))).get(); + ))); this.assertIdsGotAdded("evcs0"); @@ -198,13 +198,12 @@ public void testModifyDoubleToSingle() throws Exception { this.appManagerTestBundle.assertInstalledApps(3); this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, - new UpdateAppInstance.Request(response.instance.instanceId, "alias", JsonUtils.buildJsonObject() // + new UpdateAppInstance.Request(response.instance().instanceId, "alias", JsonUtils.buildJsonObject() // .addProperty(HardyBarthEvcs.Property.NUMBER_OF_CHARGING_STATIONS.name(), 1) // .addProperty(HardyBarthEvcs.SubPropertyFirstChargepoint.IP.name(), "2.1.1.1") // - .build())) - .get(); + .build())); - final var evcsId = response.instance.properties.get(HardyBarthEvcs.Property.EVCS_ID.name()).getAsString(); + final var evcsId = response.instance().properties.get(HardyBarthEvcs.Property.EVCS_ID.name()).getAsString(); this.assertIdsGotAdded("evcs0", evcsId); this.assertClusterHasOnlyValidProps(); @@ -219,8 +218,7 @@ public void setMaxHardwarePower() throws Exception { JsonUtils.buildJsonObject() // .addProperty(KebaEvcs.Property.IP.name(), "1.1.1.2") // .addProperty(KebaEvcs.Property.MAX_HARDWARE_POWER.name(), hardwarePower) // - .build())) - .get(); + .build())); final var clusterComponent = this.appManagerTestBundle.componentManger.getComponent("evcsCluster0"); final var hardwarePowerPerPhase = (int) clusterComponent.getComponentContext().getProperties() .get("hardwarePowerLimitPerPhase"); @@ -243,7 +241,7 @@ public void testRemoveClusterWhenEvcsBelowTwo() throws Exception { this.assertSingleClusterApp(); this.appManagerTestBundle.sut.handleDeleteAppInstanceRequest(DUMMY_ADMIN, - new DeleteAppInstance.Request(responseSecondEvcs.instance.instanceId)).get(); + new DeleteAppInstance.Request(responseSecondEvcs.instance().instanceId)); this.appManagerTestBundle.assertInstalledApps(1); } @@ -285,11 +283,10 @@ private AddAppInstance.Response installKeba(String ip) new AddAppInstance.Request(this.kebaEvcs.getAppId(), "key", "alias", // JsonUtils.buildJsonObject() // .addProperty(KebaEvcs.Property.IP.name(), ip) // - .build())) - .get(); + .build())); - final var evcsId = response.instance.properties.get(KebaEvcs.Property.EVCS_ID.name()).getAsString(); - final var evcsCtrlId = response.instance.properties.get(KebaEvcs.Property.CTRL_EVCS_ID.name()).getAsString(); + final var evcsId = response.instance().properties.get(KebaEvcs.Property.EVCS_ID.name()).getAsString(); + final var evcsCtrlId = response.instance().properties.get(KebaEvcs.Property.CTRL_EVCS_ID.name()).getAsString(); this.appManagerTestBundle.assertComponentsExist(// new EdgeConfig.Component(evcsId, null, "Evcs.Keba.KeContact", JsonUtils.buildJsonObject() // .addProperty("ip", ip) // @@ -315,11 +312,10 @@ private AddAppInstance.Response installHardyBarthDouble(String... ips) .onlyIf(count == 2, b -> b.addProperty(HardyBarthEvcs.SubPropertySecondChargepoint.IP_CP_2.name(), secondIp)) // - .build())) - .get(); + .build())); - final var evcsId = response.instance.properties.get(HardyBarthEvcs.Property.EVCS_ID.name()).getAsString(); - final var evcsCtrlId = response.instance.properties.get(HardyBarthEvcs.Property.CTRL_EVCS_ID.name()) + final var evcsId = response.instance().properties.get(HardyBarthEvcs.Property.EVCS_ID.name()).getAsString(); + final var evcsCtrlId = response.instance().properties.get(HardyBarthEvcs.Property.CTRL_EVCS_ID.name()) .getAsString(); this.appManagerTestBundle.assertComponentsExist(// new EdgeConfig.Component(evcsId, null, "Evcs.HardyBarth", JsonUtils.buildJsonObject() // @@ -329,10 +325,10 @@ private AddAppInstance.Response installHardyBarthDouble(String... ips) .build()) // ); if (count == 2) { - final var evcsIdCp2 = response.instance.properties.get(HardyBarthEvcs.Property.EVCS_ID_CP_2.name()) - .getAsString(); - final var evcsCtrlIdCp2 = response.instance.properties.get(HardyBarthEvcs.Property.CTRL_EVCS_ID_CP_2.name()) + final var evcsIdCp2 = response.instance().properties.get(HardyBarthEvcs.Property.EVCS_ID_CP_2.name()) .getAsString(); + final var evcsCtrlIdCp2 = response.instance().properties + .get(HardyBarthEvcs.Property.CTRL_EVCS_ID_CP_2.name()).getAsString(); this.appManagerTestBundle.assertComponentsExist(// new EdgeConfig.Component(evcsIdCp2, null, "Evcs.HardyBarth", JsonUtils.buildJsonObject() // .build()), // @@ -351,11 +347,10 @@ private AddAppInstance.Response installIesKeywatt() JsonUtils.buildJsonObject() // .addProperty(IesKeywattEvcs.Property.OCCP_CHARGE_POINT_IDENTIFIER.name(), "IES1") // .addProperty(IesKeywattEvcs.Property.OCCP_CONNECTOR_IDENTIFIER.name(), 1) // - .build())) - .get(); + .build())); - final var evcsId = response.instance.properties.get(IesKeywattEvcs.Property.EVCS_ID.name()).getAsString(); - final var evcsCtrlId = response.instance.properties.get(IesKeywattEvcs.Property.CTRL_EVCS_ID.name()) + final var evcsId = response.instance().properties.get(IesKeywattEvcs.Property.EVCS_ID.name()).getAsString(); + final var evcsCtrlId = response.instance().properties.get(IesKeywattEvcs.Property.CTRL_EVCS_ID.name()) .getAsString(); this.appManagerTestBundle.assertComponentsExist(// new EdgeConfig.Component(evcsId, null, "Evcs.Ocpp.IesKeywattSingle", JsonUtils.buildJsonObject() // diff --git a/io.openems.edge.core/test/io/openems/edge/app/evcs/TestHardyBarthEvcs.java b/io.openems.edge.core/test/io/openems/edge/app/evcs/TestHardyBarthEvcs.java index 9e0f1bb7ea3..435299ec387 100644 --- a/io.openems.edge.core/test/io/openems/edge/app/evcs/TestHardyBarthEvcs.java +++ b/io.openems.edge.core/test/io/openems/edge/app/evcs/TestHardyBarthEvcs.java @@ -38,13 +38,12 @@ public void testInstallationAndUpdate() throws Exception { new AddAppInstance.Request(this.hardyBarthEvcs.getAppId(), "key", "alias", JsonUtils.buildJsonObject() // .addProperty(HardyBarthEvcs.Property.NUMBER_OF_CHARGING_STATIONS.name(), 1) // .addProperty(HardyBarthEvcs.SubPropertyFirstChargepoint.IP.name(), "192.168.1.30") // - .build())) - .get(); + .build())); - final var installProps = installResponse.instance.properties; + final var installProps = installResponse.instance().properties; final var firstCreatedEvcsId = installProps.get(HardyBarthEvcs.Property.EVCS_ID.name()).getAsString(); final var firstCreatedCtrlEvcsId = installProps.get(HardyBarthEvcs.Property.CTRL_EVCS_ID.name()).getAsString(); - assertTrue(installResponse.warnings == null || installResponse.warnings.isEmpty()); + assertTrue(installResponse.warnings() == null || installResponse.warnings().isEmpty()); assertEquals(1, this.appManagerTestBundle.sut.getInstantiatedApps().size()); assertEquals("evcs0", firstCreatedEvcsId); assertEquals("ctrlEvcs0", firstCreatedCtrlEvcsId); @@ -54,21 +53,20 @@ public void testInstallationAndUpdate() throws Exception { assertFalse(installProps.has(HardyBarthEvcs.SubPropertySecondChargepoint.IP_CP_2.name())); final var updateToDoubleResponse = this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, - new UpdateAppInstance.Request(installResponse.instance.instanceId, "alias", // + new UpdateAppInstance.Request(installResponse.instance().instanceId, "alias", // JsonUtils.buildJsonObject() // .addProperty(HardyBarthEvcs.Property.NUMBER_OF_CHARGING_STATIONS.name(), 2) // .addProperty(HardyBarthEvcs.SubPropertyFirstChargepoint.IP.name(), "192.168.1.30") // .addProperty(HardyBarthEvcs.SubPropertySecondChargepoint.IP_CP_2.name(), "192.168.1.31") // .addProperty(HardyBarthEvcs.SubPropertySecondChargepoint.ALIAS_CP_2.name(), "alias 2") // - .build())) - .get(); + .build())); - final var updateDoubleProps = updateToDoubleResponse.instance.properties; + final var updateDoubleProps = updateToDoubleResponse.instance().properties; final var firstCreatedEvcsIdOfSecond = updateDoubleProps.get(HardyBarthEvcs.Property.EVCS_ID_CP_2.name()) .getAsString(); final var firstCreatedCtrlEvcsIdOfSecond = updateDoubleProps .get(HardyBarthEvcs.Property.CTRL_EVCS_ID_CP_2.name()).getAsString(); - assertTrue(updateToDoubleResponse.warnings == null || updateToDoubleResponse.warnings.isEmpty()); + assertTrue(updateToDoubleResponse.warnings() == null || updateToDoubleResponse.warnings().isEmpty()); assertEquals(1, this.appManagerTestBundle.sut.getInstantiatedApps().size()); assertEquals(firstCreatedEvcsId, updateDoubleProps.get(HardyBarthEvcs.Property.EVCS_ID.name()).getAsString()); assertEquals(firstCreatedCtrlEvcsId, @@ -83,14 +81,14 @@ public void testInstallationAndUpdate() throws Exception { assertTrue(updateDoubleProps.has(HardyBarthEvcs.SubPropertySecondChargepoint.IP_CP_2.name())); final var updateBackToSingleResponse = this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, - new UpdateAppInstance.Request(installResponse.instance.instanceId, "alias", JsonUtils.buildJsonObject() // - .addProperty(HardyBarthEvcs.Property.NUMBER_OF_CHARGING_STATIONS.name(), 1) // - .addProperty(HardyBarthEvcs.SubPropertyFirstChargepoint.IP.name(), "192.168.1.30") // - .build())) - .get(); + new UpdateAppInstance.Request(installResponse.instance().instanceId, "alias", + JsonUtils.buildJsonObject() // + .addProperty(HardyBarthEvcs.Property.NUMBER_OF_CHARGING_STATIONS.name(), 1) // + .addProperty(HardyBarthEvcs.SubPropertyFirstChargepoint.IP.name(), "192.168.1.30") // + .build())); - final var updateSingleProps = updateBackToSingleResponse.instance.properties; - assertTrue(updateBackToSingleResponse.warnings == null || updateBackToSingleResponse.warnings.isEmpty()); + final var updateSingleProps = updateBackToSingleResponse.instance().properties; + assertTrue(updateBackToSingleResponse.warnings() == null || updateBackToSingleResponse.warnings().isEmpty()); assertEquals(1, this.appManagerTestBundle.sut.getInstantiatedApps().size()); assertEquals(firstCreatedEvcsId, updateSingleProps.get(HardyBarthEvcs.Property.EVCS_ID.name()).getAsString()); assertEquals(firstCreatedEvcsId, updateSingleProps.get(HardyBarthEvcs.Property.EVCS_ID.name()).getAsString()); @@ -106,10 +104,9 @@ public void testInstallationDouble() throws Exception { .addProperty(HardyBarthEvcs.SubPropertyFirstChargepoint.IP.name(), "192.168.1.30") // .addProperty(HardyBarthEvcs.SubPropertySecondChargepoint.IP_CP_2.name(), "192.168.1.31") // .addProperty(HardyBarthEvcs.SubPropertySecondChargepoint.ALIAS_CP_2.name(), "alias 2") // - .build())) - .get(); - final var updateProps = installResponse.instance.properties; - assertTrue(installResponse.warnings == null || installResponse.warnings.isEmpty()); + .build())); + final var updateProps = installResponse.instance().properties; + assertTrue(installResponse.warnings() == null || installResponse.warnings().isEmpty()); assertEquals(1, this.appManagerTestBundle.sut.getInstantiatedApps().size()); assertTrue(updateProps.get(HardyBarthEvcs.Property.EVCS_ID.name()).getAsString().startsWith("evcs")); assertTrue(updateProps.get(HardyBarthEvcs.Property.CTRL_EVCS_ID.name()).getAsString().startsWith("ctrlEvcs")); diff --git a/io.openems.edge.core/test/io/openems/edge/app/heat/TestHeatPump.java b/io.openems.edge.core/test/io/openems/edge/app/heat/TestHeatPump.java index 07fb577c6fa..4831dc04896 100644 --- a/io.openems.edge.core/test/io/openems/edge/app/heat/TestHeatPump.java +++ b/io.openems.edge.core/test/io/openems/edge/app/heat/TestHeatPump.java @@ -86,7 +86,7 @@ public void testNotRemovingDependenciesFromRelay() throws Exception { assertEquals(3, home.dependencies.size()); // update heat pump - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, new UpdateAppInstance.Request( + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request( heatPumpInstance.instanceId, "alias", JsonUtils.buildJsonObject().build())); // if exceptions occurs here heat pump also deleted dependencies from home diff --git a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome.java b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome.java index 73a51874649..f95d671afdf 100644 --- a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome.java +++ b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome.java @@ -64,7 +64,7 @@ public void testCreateAndUpdateHomeFullSettings() throws Exception { var homeInstance = this.createFullHome(); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(homeInstance.instanceId, "aliasrename", fullConfig)); // expect the same as before // make sure every dependency got installed @@ -119,7 +119,7 @@ public void testRemoveAcMeter() throws Exception { .addProperty("SHADOW_MANAGEMENT_DISABLED", false) // .build(); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(homeInstance.instanceId, "aliasrename", configNoMeter)); // expect the same as before // make sure every dependency got installed @@ -157,7 +157,7 @@ public void testFeedInTypeRippleControlReceiver() throws Exception { final var properties = fullSettings(); properties.addProperty("RIPPLE_CONTROL_RECEIVER_ACTIV", true); this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home", "key", "alias", properties)).get(); + new AddAppInstance.Request("App.FENECON.Home", "key", "alias", properties)); final var batteryInverterProps = this.appManagerTestBundle.componentManger.getComponent("batteryInverter0") .getComponentContext().getProperties(); @@ -171,7 +171,7 @@ public void testFeedInTypeDynamicLimitation() throws Exception { final var properties = fullSettings(); properties.addProperty("FEED_IN_TYPE", FeedInType.DYNAMIC_LIMITATION.name()); this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home", "key", "alias", properties)).get(); + new AddAppInstance.Request("App.FENECON.Home", "key", "alias", properties)); final var batteryInverterProps = this.appManagerTestBundle.componentManger.getComponent("batteryInverter0") .getComponentContext().getProperties(); @@ -185,7 +185,7 @@ public void testFeedInTypeNoLimitation() throws Exception { final var properties = fullSettings(); properties.addProperty("FEED_IN_TYPE", FeedInType.NO_LIMITATION.name()); this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home", "key", "alias", properties)).get(); + new AddAppInstance.Request("App.FENECON.Home", "key", "alias", properties)); final var batteryInverterProps = this.appManagerTestBundle.componentManger.getComponent("batteryInverter0") .getComponentContext().getProperties(); @@ -211,9 +211,9 @@ public static final OpenemsAppInstance createFullHome(AppManagerTestBundle appMa var fullConfig = fullSettings(); final var response = appManagerTestBundle.sut.handleAddAppInstanceRequest(user, - new AddAppInstance.Request("App.FENECON.Home", "key", "alias", fullConfig)).get(); + new AddAppInstance.Request("App.FENECON.Home", "key", "alias", fullConfig)); - assertEquals(4, response.instance.dependencies.size()); + assertEquals(4, response.instance().dependencies.size()); // make sure every dependency got installed assertEquals(appManagerTestBundle.sut.getInstantiatedApps().size(), 5); diff --git a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome20.java b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome20.java index 53bc17e0d65..f192548fe84 100644 --- a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome20.java +++ b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome20.java @@ -49,7 +49,7 @@ public void testCreateHomeFullSettings() throws Exception { public void testCreateAndUpdateHomeFullSettings() throws Exception { var homeInstance = this.createFullHome(); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(homeInstance.instanceId, "aliasrename", fullSettings())); // expect the same as before // make sure every dependency got installed @@ -96,7 +96,7 @@ public void testCheckPvs() throws Exception { settings.addProperty("HAS_PV_3", false); settings.addProperty("HAS_PV_4", false); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(homeInstance.instanceId, "aliasrename", settings)); for (int i = 0; i < 2; i++) { @@ -124,15 +124,14 @@ public void testShadowManagement() throws Exception { .addProperty("EMERGENCY_RESERVE_ENABLED", true) // .addProperty("EMERGENCY_RESERVE_SOC", 15) // .addProperty("SHADOW_MANAGEMENT_DISABLED", true) // - .build())) - .get(); + .build())); var batteryInverter = this.appManagerTestBundle.componentManger.getComponent("batteryInverter0"); assertEquals("DISABLE", (String) batteryInverter.getComponentContext().getProperties().get("mpptForShadowEnable")); this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, - new UpdateAppInstance.Request(response.instance.instanceId, "alias", JsonUtils.buildJsonObject() // + new UpdateAppInstance.Request(response.instance().instanceId, "alias", JsonUtils.buildJsonObject() // .addProperty("SAFETY_COUNTRY", "GERMANY") // .addProperty("FEED_IN_TYPE", FeedInType.DYNAMIC_LIMITATION) // .addProperty("MAX_FEED_IN_POWER", 1000) // diff --git a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome30.java b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome30.java index 1a32aa6ffeb..ed6fd66154c 100644 --- a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome30.java +++ b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome30.java @@ -50,7 +50,7 @@ public void testCreateHomeFullSettings() throws Exception { public void testCreateAndUpdateHomeFullSettings() throws Exception { var homeInstance = this.createFullHome30(); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(homeInstance.instanceId, "aliasrename", fullSettings())); // expect the same as before // make sure every dependency got installed @@ -97,7 +97,7 @@ public void testCheckPvs() throws Exception { settings.addProperty("HAS_PV_3", false); settings.addProperty("HAS_PV_4", false); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(homeInstance.instanceId, "aliasrename", settings)); for (int i = 0; i < 2; i++) { @@ -139,7 +139,8 @@ public void testEnableEmergency() throws Exception { this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request( homeInstance.instanceId, homeInstance.alias, fullSettingsWithoutEmergencyReserve())); - this.appManagerTestBundle.scheduler.assertExactSchedulerOrder("Update Home 30 to remove EmergencyReserve Controller", + this.appManagerTestBundle.scheduler.assertExactSchedulerOrder( + "Update Home 30 to remove EmergencyReserve Controller", // "ctrlPrepareBatteryExtension0", "ctrlGridOptimizedCharge0", "ctrlEssSurplusFeedToGrid0", "ctrlBalancing0"); } @@ -156,15 +157,14 @@ public void testShadowManagement() throws Exception { .addProperty("EMERGENCY_RESERVE_ENABLED", true) // .addProperty("EMERGENCY_RESERVE_SOC", 15) // .addProperty("SHADOW_MANAGEMENT_DISABLED", true) // - .build())) - .get(); + .build())); var batteryInverter = this.appManagerTestBundle.componentManger.getComponent("batteryInverter0"); assertEquals("DISABLE", (String) batteryInverter.getComponentContext().getProperties().get("mpptForShadowEnable")); this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, - new UpdateAppInstance.Request(response.instance.instanceId, "alias", JsonUtils.buildJsonObject() // + new UpdateAppInstance.Request(response.instance().instanceId, "alias", JsonUtils.buildJsonObject() // .addProperty("SAFETY_COUNTRY", "GERMANY") // .addProperty("FEED_IN_TYPE", FeedInType.DYNAMIC_LIMITATION) // .addProperty("MAX_FEED_IN_POWER", 1000) // @@ -184,7 +184,7 @@ public void testFeedInTypeRippleControlReceiver() throws Exception { final var properties = fullSettings(); properties.addProperty("FEED_IN_TYPE", FeedInType.EXTERNAL_LIMITATION.name()); this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", properties)).get(); + new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", properties)); final var batteryInverterProps = this.appManagerTestBundle.componentManger.getComponent("batteryInverter0") .getComponentContext().getProperties(); @@ -198,7 +198,7 @@ public void testFeedInTypeDynamicLimitation() throws Exception { final var properties = fullSettings(); properties.addProperty("FEED_IN_TYPE", FeedInType.DYNAMIC_LIMITATION.name()); this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", properties)).get(); + new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", properties)); final var batteryInverterProps = this.appManagerTestBundle.componentManger.getComponent("batteryInverter0") .getComponentContext().getProperties(); @@ -212,7 +212,7 @@ public void testFeedInTypeNoLimitation() throws Exception { final var properties = fullSettings(); properties.addProperty("FEED_IN_TYPE", FeedInType.NO_LIMITATION.name()); this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", properties)).get(); + new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", properties)); final var batteryInverterProps = this.appManagerTestBundle.componentManger.getComponent("batteryInverter0") .getComponentContext().getProperties(); @@ -238,9 +238,9 @@ public static final OpenemsAppInstance createFullHome30(AppManagerTestBundle app var fullConfig = fullSettings(); final var response = appManagerTestBundle.sut.handleAddAppInstanceRequest(user, - new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", fullConfig)).get(); + new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", fullConfig)); - assertEquals(4, response.instance.dependencies.size()); + assertEquals(4, response.instance().dependencies.size()); // make sure every dependency got installed assertEquals(appManagerTestBundle.sut.getInstantiatedApps().size(), 5); @@ -267,7 +267,8 @@ public static final OpenemsAppInstance createFullHome30(AppManagerTestBundle app assertNotNull(homeInstance); appManagerTestBundle.assertNoValidationErrors(); - appManagerTestBundle.scheduler.assertExactSchedulerOrder("Failed setting initial Home 30 Scheduler configuration", + appManagerTestBundle.scheduler.assertExactSchedulerOrder( + "Failed setting initial Home 30 Scheduler configuration", // "ctrlPrepareBatteryExtension0", "ctrlEmergencyCapacityReserve0", "ctrlGridOptimizedCharge0", "ctrlEssSurplusFeedToGrid0", "ctrlBalancing0"); return homeInstance; diff --git a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome30DefaultRelays.java b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome30DefaultRelays.java index 0b96ddc28a3..699921aa573 100644 --- a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome30DefaultRelays.java +++ b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHome30DefaultRelays.java @@ -94,7 +94,7 @@ public void testDefaultRelayValuesThresholdControl() throws Exception { private final OpenemsAppInstance createFullHomeWithDummyIo() throws Exception { final var instance = TestFeneconHome30.createFullHome30(this.appManagerTestBundle, DUMMY_ADMIN); - this.appManagerTestBundle.componentManger.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.componentManger.handleDeleteComponentConfigRequest(DUMMY_ADMIN, new DeleteComponentConfigRequest("io0")); final var dummyRelay = new DummyInputOutput("io0", "RELAY", 1, 8); this.appManagerTestBundle.cm.getOrCreateEmptyConfiguration(dummyRelay.id()); diff --git a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHomeDefaultRelays.java b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHomeDefaultRelays.java index 294a053a360..983785d6092 100644 --- a/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHomeDefaultRelays.java +++ b/io.openems.edge.core/test/io/openems/edge/app/integratedsystem/TestFeneconHomeDefaultRelays.java @@ -90,7 +90,7 @@ public void testDefaultRelayValuesThresholdControl() throws Exception { private final OpenemsAppInstance createFullHomeWithDummyIo() throws Exception { final var instance = TestFeneconHome.createFullHome(this.appManagerTestBundle, DUMMY_ADMIN); - this.appManagerTestBundle.componentManger.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.componentManger.handleDeleteComponentConfigRequest(DUMMY_ADMIN, new DeleteComponentConfigRequest("io0")); final var dummyRelay = new DummyInputOutput("io0", "RELAY", 1, 4); this.appManagerTestBundle.cm.getOrCreateEmptyConfiguration(dummyRelay.id()); diff --git a/io.openems.edge.core/test/io/openems/edge/app/timeofusetariff/TestTibber.java b/io.openems.edge.core/test/io/openems/edge/app/timeofusetariff/TestTibber.java index 63d398608c1..da9a0d9deec 100644 --- a/io.openems.edge.core/test/io/openems/edge/app/timeofusetariff/TestTibber.java +++ b/io.openems.edge.core/test/io/openems/edge/app/timeofusetariff/TestTibber.java @@ -58,15 +58,15 @@ public void testRemoveAccessToken() throws Exception { .addProperty("ACCESS_TOKEN", "g78aw9ht2n112nb453") // .build(); var response = this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request(this.tibber.getAppId(), "key", "alias", properties)).get(); + new AddAppInstance.Request(this.tibber.getAppId(), "key", "alias", properties)); // in response its set because the access token in the component is not empty - assertEquals("xxx", response.instance.properties.get("ACCESS_TOKEN").getAsString()); + assertEquals("xxx", response.instance().properties.get("ACCESS_TOKEN").getAsString()); // in the actual instance there shouldn't be an access token, instead it should // only be taken directly from the component final var instance = this.appManagerTestBundle.appManagerUtil - .findInstanceByIdOrError(response.instance.instanceId); + .findInstanceByIdOrError(response.instance().instanceId); assertFalse(instance.properties.has("ACCESS_TOKEN")); final var apps = this.appManagerTestBundle.getAppsFromConfig(); @@ -89,7 +89,7 @@ public void testAddChannelToPredictor() throws Exception { .addProperty("ACCESS_TOKEN", "g78aw9ht2n112nb453") // .build(); this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request(this.tibber.getAppId(), "key", "alias", properties)).get(); + new AddAppInstance.Request(this.tibber.getAppId(), "key", "alias", properties)); this.assertChannelsInPredictor("_sum/UnmanagedConsumptionActivePower"); } @@ -100,7 +100,7 @@ public void testOnlyCompatibleWithHome() throws Exception { .addProperty("ACCESS_TOKEN", "g78aw9ht2n112nb453") // .build(); this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request(this.tibber.getAppId(), "key", "alias", properties)).get(); + new AddAppInstance.Request(this.tibber.getAppId(), "key", "alias", properties)); } @Test @@ -110,40 +110,69 @@ public void testSetTokenValue() throws Exception { .addProperty("ACCESS_TOKEN", "g78aw9ht2n112nb453") // .build(); final var response = this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request(this.tibber.getAppId(), "key", "alias", properties)).get(); + new AddAppInstance.Request(this.tibber.getAppId(), "key", "alias", properties)); final var accessTokenProp = Arrays.stream(this.tibber.getProperties()) // .filter(t -> t.name.equals(Tibber.Property.ACCESS_TOKEN.name())) // .findAny().orElse(null); - var value = accessTokenProp.bidirectionalValue.apply(response.instance.properties); + var value = accessTokenProp.bidirectionalValue.apply(response.instance().properties); assertEquals("xxx", value.getAsString()); - this.appManagerTestBundle.componentManger.handleJsonrpcRequest(DUMMY_ADMIN, - new UpdateComponentConfigRequest(response.instance.properties + this.appManagerTestBundle.componentManger.handleUpdateComponentConfigRequest(DUMMY_ADMIN, + new UpdateComponentConfigRequest(response.instance().properties .get(Tibber.Property.TIME_OF_USE_TARIFF_PROVIDER_ID.name()).getAsString(), - List.of(new UpdateComponentConfigRequest.Property("accessToken", "")))) - .get(); + List.of(new UpdateComponentConfigRequest.Property("accessToken", "")))); - value = accessTokenProp.bidirectionalValue.apply(response.instance.properties); + value = accessTokenProp.bidirectionalValue.apply(response.instance().properties); assertEquals(JsonNull.INSTANCE, value); } + @Test + public void testUnsetFilterValue() throws Exception { + this.installHome(); + final var properties = JsonUtils.buildJsonObject() // + .addProperty(Tibber.Property.ACCESS_TOKEN.name(), "g78aw9ht2n112nb453") // + .addProperty(Tibber.Property.MULTIPLE_HOMES_CHECK.name(), true) // + .addProperty(Tibber.Property.FILTER.name(), "randomInitialFilter") // + .build(); + final var response = this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, + new AddAppInstance.Request(this.tibber.getAppId(), "key", "alias", properties)); + + final var filterProp = Arrays.stream(this.tibber.getProperties()) // + .filter(t -> t.name.equals(Tibber.Property.FILTER.name())) // + .findAny().orElseThrow(); + var value = filterProp.bidirectionalValue.apply(response.instance().properties); + + assertEquals("randomInitialFilter", value.getAsString()); + + this.appManagerTestBundle.componentManger.handleUpdateComponentConfigRequest(DUMMY_ADMIN, + new UpdateComponentConfigRequest( + response.instance().properties.get(Tibber.Property.TIME_OF_USE_TARIFF_PROVIDER_ID.name()) + .getAsString(), + List.of(new UpdateComponentConfigRequest.Property(Tibber.Property.ACCESS_TOKEN.name(), + "g78aw9ht2n112nb453")))); + + value = filterProp.bidirectionalValue.apply(response.instance().properties); + + assertTrue(value.isJsonPrimitive()); + assertTrue(value.getAsJsonPrimitive().isString()); + assertEquals("", value.getAsString()); + } + private void createPredictor() throws Exception { - this.appManagerTestBundle.componentManger.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.componentManger.handleCreateComponentConfigRequest(DUMMY_ADMIN, new CreateComponentConfigRequest("Predictor.PersistenceModel", List.of(// new UpdateComponentConfigRequest.Property("id", "predictor0"), // new UpdateComponentConfigRequest.Property("channelAddresses", JsonUtils.buildJsonArray()// .build()) // - ))).get(); + ))); } private void installHome() throws InterruptedException, ExecutionException, OpenemsNamedException { - this.appManagerTestBundle.sut - .handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home", "key", "alias", TestFeneconHome.minSettings())) - .get(); + this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, + new AddAppInstance.Request("App.FENECON.Home", "key", "alias", TestFeneconHome.minSettings())); } private void assertChannelsInPredictor(String... channels) throws OpenemsNamedException { diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerAppHelperImplTest.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerAppHelperImplTest.java index 22899557932..98637a96d29 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerAppHelperImplTest.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerAppHelperImplTest.java @@ -2,6 +2,7 @@ import static io.openems.edge.common.test.DummyUser.DUMMY_ADMIN; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.ArrayList; @@ -130,7 +131,7 @@ public void testUpdatePolicyNever() throws OpenemsNamedException { assertEquals(2, this.appManagerTestBundle.sut.getInstantiatedApps().size()); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(this.getAppByAppId(this.testAApp.getAppId()).instanceId, "", JsonUtils.buildJsonObject() // .addProperty("UPDATE_POLICY", DependencyDeclaration.UpdatePolicy.NEVER.name()) // @@ -160,7 +161,7 @@ public void testUpdatePolicyAlways() throws OpenemsNamedException { assertEquals(3, this.appManagerTestBundle.sut.getInstantiatedApps().size()); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(this.getAppByAppId(this.testAApp.getAppId()).instanceId, "", JsonUtils.buildJsonObject() // .addProperty("UPDATE_POLICY", DependencyDeclaration.UpdatePolicy.ALWAYS.name()) // @@ -184,7 +185,7 @@ public void testUpdatePolicyIfMine() throws OpenemsNamedException { assertEquals(2, this.appManagerTestBundle.sut.getInstantiatedApps().size()); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(this.getAppByAppId(this.testAApp.getAppId()).instanceId, "", JsonUtils.buildJsonObject() // .addProperty("UPDATE_POLICY", DependencyDeclaration.UpdatePolicy.IF_MINE.name()) // @@ -200,7 +201,7 @@ public void testUpdatePolicyIfMine() throws OpenemsNamedException { assertEquals(3, this.appManagerTestBundle.sut.getInstantiatedApps().size()); - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(this.getAppByAppId(this.testAApp.getAppId()).instanceId, "", JsonUtils.buildJsonObject() // .addProperty("UPDATE_POLICY", DependencyDeclaration.UpdatePolicy.IF_MINE.name()) // @@ -334,14 +335,13 @@ public void testDependencyUpdatePolicyAllowAll() assertEquals(2, this.appManagerTestBundle.sut.getInstantiatedApps().size()); var newAlias = "newAppAlias"; - var completable = this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + var result = this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(this.getAppByAppId(this.testCApp.getAppId()).instanceId, newAlias, JsonUtils.buildJsonObject() // .addProperty("NUMBER", 2) // .build())); - var result = completable.get().getResult(); - assertTrue(!result.has("warnings") || result.get("warnings").getAsJsonArray().size() == 0); + assertTrue(result.warnings().isEmpty()); var instance = this.getAppByAppId(this.testCApp.getAppId()); assertEquals(newAlias, instance.alias); @@ -364,7 +364,7 @@ public void testDependencyUpdatePolicyAllowNone() assertEquals(2, this.appManagerTestBundle.sut.getInstantiatedApps().size()); var newAlias = "newAppAlias"; - this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(this.getAppByAppId(this.testCApp.getAppId()).instanceId, newAlias, JsonUtils.buildJsonObject() // .addProperty("NUMBER", 2) // @@ -387,15 +387,13 @@ public void testDependencyUpdatePolicyAllowOnlyUnconfiguredProperties() assertEquals(2, this.appManagerTestBundle.sut.getInstantiatedApps().size()); var newAlias = "newAppAlias"; - var completable = this.appManagerTestBundle.sut.handleJsonrpcRequest(DUMMY_ADMIN, + var result = this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(this.getAppByAppId(this.testCApp.getAppId()).instanceId, newAlias, JsonUtils.buildJsonObject() // .addProperty("NUMBER", 2) // .build())); - var result = completable.get().getResult(); - assertTrue(result.has("warnings")); - assertTrue(result.get("warnings").getAsJsonArray().size() > 0); + assertFalse(result.warnings().isEmpty()); var instance = this.getAppByAppId(this.testCApp.getAppId()); assertEquals(newAlias, instance.alias); diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerImpSynchronizationTest.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerImpSynchronizationTest.java index 7bdc17916be..9b79d51b330 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerImpSynchronizationTest.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerImpSynchronizationTest.java @@ -6,7 +6,6 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.junit.Before; @@ -142,8 +141,7 @@ public void testRemoveOfNotAvailableInstance() throws Exception { public void testSimulateAfterInstallaion() throws Exception { this.appManager.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request("App.PvInverter.SolarEdge", "key", "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); + .build())); assertTrue(this.appManager.lockModifyingApps.tryLock()); assertTrue(this.appManager.waitingForModified); @@ -156,8 +154,7 @@ public void testSimulateAfterInstallaion() throws Exception { public void testSimulateLockWaitingForModification() throws Exception { this.appManager.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request("App.PvInverter.SolarEdge", "key", "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); + .build())); assertTrue(this.appManager.waitingForModified); @@ -166,9 +163,8 @@ public void testSimulateLockWaitingForModification() throws Exception { return this.appManager.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request("App.PvInverter.SolarEdge", "key", "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); - } catch (InterruptedException | ExecutionException | OpenemsNamedException e) { + .build())); + } catch (OpenemsNamedException e) { throw new RuntimeException(e); } }); diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerTestBundle.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerTestBundle.java index 97ca72d496d..abcc772227d 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerTestBundle.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppManagerTestBundle.java @@ -8,7 +8,6 @@ import java.util.Dictionary; import java.util.Hashtable; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -29,7 +28,6 @@ import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.types.EdgeConfig; import io.openems.common.utils.JsonUtils; import io.openems.common.utils.ReflectionUtils; @@ -181,7 +179,7 @@ protected void deactivate() { } @Override - public CompletableFuture handleAddAppInstanceRequest(User user, Request request, + public AddAppInstance.Response handleAddAppInstanceRequest(User user, Request request, boolean ignoreBackend) throws OpenemsNamedException { final var response = super.handleAddAppInstanceRequest(user, request, ignoreBackend); this.modifyWithCurrentConfig(); @@ -189,7 +187,7 @@ public CompletableFuture handleAddAppInstanceRequest(Us } @Override - public CompletableFuture handleDeleteAppInstanceRequest(User user, + public DeleteAppInstance.Response handleDeleteAppInstanceRequest(User user, DeleteAppInstance.Request request) throws OpenemsNamedException { final var response = super.handleDeleteAppInstanceRequest(user, request); this.modifyWithCurrentConfig(); @@ -197,7 +195,7 @@ public CompletableFuture handleDeleteAppInstan } @Override - public CompletableFuture handleUpdateAppInstanceRequest(User user, + public UpdateAppInstance.Response handleUpdateAppInstanceRequest(User user, UpdateAppInstance.Request request) throws OpenemsNamedException { final var response = super.handleUpdateAppInstanceRequest(user, request); this.modifyWithCurrentConfig(); diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppPermissionTest.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppPermissionTest.java index bb0be50560b..7156716fbd6 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppPermissionTest.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/AppPermissionTest.java @@ -61,11 +61,11 @@ private OpenemsAppInstance createDummyApp() throws Exception { return this.test.sut.handleAddAppInstanceRequest(DUMMY_INSTALLER, new AddAppInstance.Request("App.Dummy", "key", "alias", JsonUtils.buildJsonObject() // .build())) - .get().instance; + .instance(); } private void deleteDummyApp(OpenemsAppInstance instance, User user) throws Exception { - this.test.sut.handleDeleteAppInstanceRequest(user, new DeleteAppInstance.Request(instance.instanceId)).get(); + this.test.sut.handleDeleteAppInstanceRequest(user, new DeleteAppInstance.Request(instance.instanceId)); } } diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/DummyPseudoComponentManager.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/DummyPseudoComponentManager.java index 36f66c5f4ef..9aba2d766eb 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/DummyPseudoComponentManager.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/DummyPseudoComponentManager.java @@ -11,7 +11,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Vector; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -30,13 +29,9 @@ import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.jsonrpc.base.GenericJsonrpcResponseSuccess; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest; -import io.openems.common.session.Role; import io.openems.common.types.EdgeConfig; import io.openems.common.types.EdgeConfig.ActualEdgeConfig; import io.openems.common.types.EdgeConfig.Component; @@ -157,27 +152,7 @@ public EdgeConfig getEdgeConfig() { } @Override - public CompletableFuture handleJsonrpcRequest(// - final User user, // - final JsonrpcRequest request // - ) throws OpenemsNamedException { - user.assertRoleIsAtLeast("handleJsonrpcRequest", Role.GUEST); - - switch (request.getMethod()) { - - case CreateComponentConfigRequest.METHOD: - return this.handleCreateComponentConfigRequest(user, CreateComponentConfigRequest.from(request)); - case UpdateComponentConfigRequest.METHOD: - return this.handleUpdateComponentConfigRequest(user, UpdateComponentConfigRequest.from(request)); - case DeleteComponentConfigRequest.METHOD: - return this.handleDeleteComponentConfigRequest(user, DeleteComponentConfigRequest.from(request)); - - default: - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } - } - - private CompletableFuture handleCreateComponentConfigRequest(// + public void handleCreateComponentConfigRequest(// final User user, // final CreateComponentConfigRequest request // ) throws OpenemsNamedException { @@ -189,11 +164,10 @@ private CompletableFuture handleCreateComponentConfigReq ); this.components.add(component); - - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); } - private CompletableFuture handleUpdateComponentConfigRequest(// + @Override + public void handleUpdateComponentConfigRequest(// final User user, // final UpdateComponentConfigRequest request // ) throws OpenemsNamedException { @@ -207,7 +181,6 @@ private CompletableFuture handleUpdateComponentConfigReq ); this.components.removeIf(t -> t.id().equals(request.getComponentId())); this.components.add(component); - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); } if (this.configurationAdmin == null) { throw new OpenemsException("Can not update Component Config. ConfigurationAdmin is null!"); @@ -227,18 +200,17 @@ private CompletableFuture handleUpdateComponentConfigReq } configuration.update(properties); } - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); } catch (IOException | InvalidSyntaxException e) { throw new OpenemsException("Can not update Component Config."); } } - private CompletableFuture handleDeleteComponentConfigRequest(// + @Override + public void handleDeleteComponentConfigRequest(// final User user, // final DeleteComponentConfigRequest request // ) throws OpenemsNamedException { this.components.removeIf(t -> t.id().equals(request.getComponentId())); - return CompletableFuture.completedFuture(new GenericJsonrpcResponseSuccess(request.getId())); } /** @@ -417,4 +389,4 @@ public ServiceReference getServiceReference() { } -} \ No newline at end of file +} diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/InstallationTest.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/InstallationTest.java index abefe8a1024..6824b806e5e 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/InstallationTest.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/InstallationTest.java @@ -31,8 +31,7 @@ public void testIncompatibleAppInstallation() throws Exception { try { appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request(app.getAppId(), "key", "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); + .build())); } catch (OpenemsException e) { exception = e; } @@ -51,11 +50,10 @@ public void testCompatibleAppInstallation() throws Exception { singleAppTest(dummyApp, (appManagerTestBundle, app) -> { final var response = appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request(app.getAppId(), "key", "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); + .build())); assertNotNull(appManagerTestBundle.sut.getInstantiatedApps() - .get(appManagerTestBundle.sut.getInstantiatedApps().indexOf(response.instance))); + .get(appManagerTestBundle.sut.getInstantiatedApps().indexOf(response.instance()))); }); } @@ -70,8 +68,7 @@ public void testNotInstallableAppInstallation() throws Exception { try { appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request(app.getAppId(), "key", "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); + .build())); } catch (OpenemsException e) { exception = e; } @@ -90,11 +87,10 @@ public void testInstallableAppInstallation() throws Exception { singleAppTest(dummyApp, (appManagerTestBundle, app) -> { final var response = appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request(app.getAppId(), "key", "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); + .build())); assertNotNull(appManagerTestBundle.sut.getInstantiatedApps() - .get(appManagerTestBundle.sut.getInstantiatedApps().indexOf(response.instance))); + .get(appManagerTestBundle.sut.getInstantiatedApps().indexOf(response.instance()))); }); } @@ -110,8 +106,7 @@ public void testAppInstallationWithExceptionalConfiguration() throws Exception { try { appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request(app.getAppId(), "key", "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); + .build())); } catch (OpenemsException e) { exception = e; } @@ -133,11 +128,10 @@ public void testAppUpdateWithExceptionalConfiguration() throws Exception { singleAppTest(dummyApp, (appManagerTestBundle, app) -> { final var response = appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request(app.getAppId(), "key", "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); + .build())); final var instance = appManagerTestBundle.sut.getInstantiatedApps() - .get(appManagerTestBundle.sut.getInstantiatedApps().indexOf(response.instance)); + .get(appManagerTestBundle.sut.getInstantiatedApps().indexOf(response.instance())); assertNotNull(instance); @@ -145,8 +139,7 @@ public void testAppUpdateWithExceptionalConfiguration() throws Exception { try { appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(instance.instanceId, "alias", JsonUtils.buildJsonObject() // - .build())) - .get(); + .build())); } catch (OpenemsException e) { exception = e; } diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/TestSettingComponentIds.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/TestSettingComponentIds.java index 9e9df0f93e2..5538b1cb389 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/TestSettingComponentIds.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/TestSettingComponentIds.java @@ -36,7 +36,7 @@ public void beforeEach() throws Exception { public void testSettingInitially() throws Exception { final var installResponse = this.add(4); - final var installProps = installResponse.instance.properties; + final var installProps = installResponse.instance().properties; final var initId1 = JsonUtils.getAsString(installProps, TestMultipleIds.Property.ID_1.name()); final var initId2 = JsonUtils.getAsString(installProps, TestMultipleIds.Property.ID_2.name()); final var initId3 = JsonUtils.getAsString(installProps, TestMultipleIds.Property.ID_3.name()); @@ -49,14 +49,14 @@ public void testSettingInitially() throws Exception { public void testSettingOnUpdate() throws Exception { final var installResponse = this.add(2); - final var installProps = installResponse.instance.properties; + final var installProps = installResponse.instance().properties; final var initId1 = JsonUtils.getAsString(installProps, TestMultipleIds.Property.ID_1.name()); final var initId2 = JsonUtils.getAsString(installProps, TestMultipleIds.Property.ID_2.name()); assertEquals(2, Sets.newHashSet(initId1, initId2).size()); assertEquals(3, installProps.size()); - final var updateResponse = this.update(installResponse.instance.instanceId, 4); - final var updateProps = updateResponse.instance.properties; + final var updateResponse = this.update(installResponse.instance().instanceId, 4); + final var updateProps = updateResponse.instance().properties; final var updatedId1 = JsonUtils.getAsString(updateProps, TestMultipleIds.Property.ID_1.name()); final var updatedId2 = JsonUtils.getAsString(updateProps, TestMultipleIds.Property.ID_2.name()); final var initId3 = JsonUtils.getAsString(updateProps, TestMultipleIds.Property.ID_3.name()); @@ -72,7 +72,7 @@ public void testSettingOnUpdate() throws Exception { public void testRemoveIds() throws Exception { final var installResponse = this.add(4); - final var installProps = installResponse.instance.properties; + final var installProps = installResponse.instance().properties; final var initId1 = JsonUtils.getAsString(installProps, TestMultipleIds.Property.ID_1.name()); final var initId2 = JsonUtils.getAsString(installProps, TestMultipleIds.Property.ID_2.name()); final var initId3 = JsonUtils.getAsString(installProps, TestMultipleIds.Property.ID_3.name()); @@ -80,8 +80,8 @@ public void testRemoveIds() throws Exception { assertEquals(4, Sets.newHashSet(initId1, initId2, initId3, initId4).size()); - final var updateResponse = this.update(installResponse.instance.instanceId, 2); - final var updateProps = updateResponse.instance.properties; + final var updateResponse = this.update(installResponse.instance().instanceId, 2); + final var updateProps = updateResponse.instance().properties; final var updatedId1 = JsonUtils.getAsString(updateProps, TestMultipleIds.Property.ID_1.name()); final var updatedId2 = JsonUtils.getAsString(updateProps, TestMultipleIds.Property.ID_2.name()); @@ -95,16 +95,14 @@ private AddAppInstance.Response add(int setIds) throws Exception { return this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, new AddAppInstance.Request(this.testMultipleIds.getAppId(), "key", "alias", JsonUtils.buildJsonObject() // .addProperty(TestMultipleIds.Property.SET_IDS.name(), setIds) // - .build())) - .get(); + .build())); } private UpdateAppInstance.Response update(UUID instanceId, int setIds) throws Exception { return this.appManagerTestBundle.sut.handleUpdateAppInstanceRequest(DUMMY_ADMIN, new UpdateAppInstance.Request(instanceId, "alias", JsonUtils.buildJsonObject() // .addProperty(TestMultipleIds.Property.SET_IDS.name(), setIds) // - .build())) - .get(); + .build())); } } diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/ComponentAggregateTaskImplTest.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/ComponentAggregateTaskImplTest.java index d10837e575b..73543c97028 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/ComponentAggregateTaskImplTest.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/ComponentAggregateTaskImplTest.java @@ -17,6 +17,7 @@ import io.openems.common.session.Language; import io.openems.common.types.EdgeConfig; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.test.DummyConfigurationAdmin; import io.openems.edge.core.appmanager.AppConfiguration; import io.openems.edge.core.appmanager.DummyPseudoComponentManager; import io.openems.edge.core.appmanager.TranslationUtil; @@ -30,6 +31,7 @@ public class ComponentAggregateTaskImplTest { @Before public void setUp() throws Exception { this.componentManager = new DummyPseudoComponentManager(); + this.componentManager.setConfigurationAdmin(new DummyConfigurationAdmin()); this.task = new ComponentAggregateTaskImpl(this.componentManager); this.task.reset(); } diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/PersistencePredictorAggregateTaskImplTest.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/PersistencePredictorAggregateTaskImplTest.java index 05ec348ff7f..abd53f85834 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/PersistencePredictorAggregateTaskImplTest.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/PersistencePredictorAggregateTaskImplTest.java @@ -21,6 +21,7 @@ import io.openems.common.session.Language; import io.openems.common.types.EdgeConfig; import io.openems.common.utils.JsonUtils; +import io.openems.edge.common.test.DummyConfigurationAdmin; import io.openems.edge.core.appmanager.AppConfiguration; import io.openems.edge.core.appmanager.DummyPseudoComponentManager; import io.openems.edge.core.appmanager.TranslationUtil; @@ -34,6 +35,7 @@ public class PersistencePredictorAggregateTaskImplTest { @Before public void setUp() throws Exception { this.componentManager = new DummyPseudoComponentManager(); + this.componentManager.setConfigurationAdmin(new DummyConfigurationAdmin()); this.task = new PersistencePredictorAggregateTaskImpl(this.componentManager); this.task.reset(); } diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/SchedulerByCentralOrderAggregateTaskImplTest.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/SchedulerByCentralOrderAggregateTaskImplTest.java index b692317f4bf..d98e708f505 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/SchedulerByCentralOrderAggregateTaskImplTest.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/SchedulerByCentralOrderAggregateTaskImplTest.java @@ -274,7 +274,7 @@ public void testGetGeneralFailMessage() { @Test public void testValidate() throws Exception { this.testBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("appId", "key", "alias", JsonUtils.buildJsonObject().build())).get(); + new AddAppInstance.Request("appId", "key", "alias", JsonUtils.buildJsonObject().build())); final var schedulerConfig = new SchedulerByCentralOrderConfiguration(// new SchedulerComponent("id0", "factoryId", "appId") // @@ -292,7 +292,7 @@ public void testValidate() throws Exception { @Test public void testValidateMissingIds() throws Exception { this.testBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("appId", "key", "alias", JsonUtils.buildJsonObject().build())).get(); + new AddAppInstance.Request("appId", "key", "alias", JsonUtils.buildJsonObject().build())); // remove ids from scheduler this.testBundle.scheduler.setSchedulerIds(DUMMY_ADMIN); @@ -313,11 +313,9 @@ public void testValidateMissingIds() throws Exception { @Test public void testValidateWronglyConfiguredIds() throws Exception { this.testBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("appId", "key", "alias", JsonUtils.buildJsonObject().build())).get(); - this.testBundle.sut - .handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("appId2", "key", "alias", JsonUtils.buildJsonObject().build())) - .get(); + new AddAppInstance.Request("appId", "key", "alias", JsonUtils.buildJsonObject().build())); + this.testBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, + new AddAppInstance.Request("appId2", "key", "alias", JsonUtils.buildJsonObject().build())); this.testBundle.scheduler.setSchedulerIds(DUMMY_ADMIN, "id1", "id0"); diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/TestScheduler.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/TestScheduler.java index 8ce40d13df9..05708d430bd 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/TestScheduler.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/dependency/aggregatetask/TestScheduler.java @@ -81,13 +81,13 @@ public void setSchedulerIds(User user, List ids) this.setSchedulerIds(user, ids.toArray(String[]::new)); } - public void setSchedulerIds(User user, String... ids) - throws OpenemsNamedException, InterruptedException, ExecutionException { - this.componentManager.handleJsonrpcRequest(user, new UpdateComponentConfigRequest("scheduler0", List.of(// - new UpdateComponentConfigRequest.Property("controllers.ids", Arrays.stream(ids) // - .map(JsonPrimitive::new) // - .collect(toJsonArray())) // - ))).get(); + public void setSchedulerIds(User user, String... ids) throws OpenemsNamedException { + this.componentManager.handleUpdateComponentConfigRequest(user, + new UpdateComponentConfigRequest("scheduler0", List.of(// + new UpdateComponentConfigRequest.Property("controllers.ids", Arrays.stream(ids) // + .map(JsonPrimitive::new) // + .collect(toJsonArray())) // + ))); } } diff --git a/io.openems.edge.core/test/io/openems/edge/core/appmanager/validator/CheckHomeTest.java b/io.openems.edge.core/test/io/openems/edge/core/appmanager/validator/CheckHomeTest.java index 578d5281b31..6174b2d811d 100644 --- a/io.openems.edge.core/test/io/openems/edge/core/appmanager/validator/CheckHomeTest.java +++ b/io.openems.edge.core/test/io/openems/edge/core/appmanager/validator/CheckHomeTest.java @@ -47,12 +47,10 @@ public void testCheck() { @Test public void testCheckWithInstalledHome10() throws Exception { - final var response = this.appManagerTestBundle.sut - .handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home", "key", "alias", TestFeneconHome.fullSettings())) - .get(); + final var response = this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, + new AddAppInstance.Request("App.FENECON.Home", "key", "alias", TestFeneconHome.fullSettings())); - assertTrue(response.warnings.isEmpty()); + assertTrue(response.warnings().isEmpty()); assertTrue(this.checkHome.check()); assertTrue(PropsUtil.isHomeInstalled(this.appManagerTestBundle.appManagerUtil)); } @@ -60,10 +58,9 @@ public void testCheckWithInstalledHome10() throws Exception { @Test public void testCheckWithInstalledHome20() throws Exception { final var response = this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home.20", "key", "alias", TestFeneconHome20.fullSettings())) - .get(); + new AddAppInstance.Request("App.FENECON.Home.20", "key", "alias", TestFeneconHome20.fullSettings())); - assertTrue(response.warnings.isEmpty()); + assertTrue(response.warnings().isEmpty()); assertTrue(this.checkHome.check()); assertTrue(PropsUtil.isHomeInstalled(this.appManagerTestBundle.appManagerUtil)); } @@ -71,10 +68,9 @@ public void testCheckWithInstalledHome20() throws Exception { @Test public void testCheckWithInstalledHome30() throws Exception { final var response = this.appManagerTestBundle.sut.handleAddAppInstanceRequest(DUMMY_ADMIN, - new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", TestFeneconHome30.fullSettings())) - .get(); + new AddAppInstance.Request("App.FENECON.Home.30", "key", "alias", TestFeneconHome30.fullSettings())); - assertTrue(response.warnings.isEmpty()); + assertTrue(response.warnings().isEmpty()); assertTrue(this.checkHome.check()); assertTrue(PropsUtil.isHomeInstalled(this.appManagerTestBundle.appManagerUtil)); } diff --git a/io.openems.edge.edge2edge/src/io/openems/edge/edge2edge/common/AbstractEdge2Edge.java b/io.openems.edge.edge2edge/src/io/openems/edge/edge2edge/common/AbstractEdge2Edge.java index e3a922e5a58..9dae53bdbeb 100644 --- a/io.openems.edge.edge2edge/src/io/openems/edge/edge2edge/common/AbstractEdge2Edge.java +++ b/io.openems.edge.edge2edge/src/io/openems/edge/edge2edge/common/AbstractEdge2Edge.java @@ -1,5 +1,8 @@ package io.openems.edge.edge2edge.common; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; +import static java.util.concurrent.CompletableFuture.completedFuture; + import java.util.ArrayDeque; import java.util.Deque; import java.util.List; @@ -50,7 +53,7 @@ public abstract class AbstractEdge2Edge extends AbstractOpenemsModbusComponent protected AbstractEdge2Edge(List> modbusSlaveNatureTableMethods, io.openems.edge.common.channel.ChannelId[] firstInitialChannelIds, - io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) throws OpenemsException { + io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) { super(firstInitialChannelIds, furtherInitialChannelIds); this.modbusSlaveNatureTableMethods = modbusSlaveNatureTableMethods; this.modbusProtocol = new ModbusProtocol(this); @@ -76,37 +79,33 @@ protected boolean activate(ComponentContext context, String id, String alias, bo return; } - try { - ModbusUtils.readELementOnce(this.modbusProtocol, new UnsignedWordElement(1), true).thenAccept(value -> { - if (value == null) { - return; - } - this.findComponentBlock(remoteComponentId, 1 + value) // - .whenComplete((startAddress, e1) -> { - if (e1 != null) { - this._setMappingRemoteProtocolFault(true); - e1.printStackTrace(); - return; - } - - // Found Component Block -> read each nature block - this.readNatureBlocks(startAddress).whenComplete((ignore, e2) -> { - if (e2 != null) { + readElementOnce(this.modbusProtocol, ModbusUtils::retryOnNull, new UnsignedWordElement(1)) + .thenAccept(value -> { + if (value == null) { + return; + } + this.findComponentBlock(remoteComponentId, 1 + value) // + .whenComplete((startAddress, e1) -> { + if (e1 != null) { this._setMappingRemoteProtocolFault(true); - // TODO restart with timeout finding the component block on exception - e2.printStackTrace(); + e1.printStackTrace(); return; } - this._setMappingRemoteProtocolFault(false); - this.logInfo(this.log, "Finished reading remote Modbus/TCP protocol"); - }); - }); - }); + // Found Component Block -> read each nature block + this.readNatureBlocks(startAddress).whenComplete((ignore, e2) -> { + if (e2 != null) { + this._setMappingRemoteProtocolFault(true); + // TODO restart with timeout finding the component block on exception + e2.printStackTrace(); + return; + } - } catch (OpenemsException e) { - this._setMappingRemoteProtocolFault(true); - } + this._setMappingRemoteProtocolFault(false); + this.logInfo(this.log, "Finished reading remote Modbus/TCP protocol"); + }); + }); + }); }); return false; } @@ -123,14 +122,10 @@ protected void deactivate() { * Tests if first register is 0x6201 ("OpenEMS"). * * @return a future true if it is OpenEMS; otherwise false - * @throws OpenemsException on error */ - private CompletableFuture isOpenems() throws OpenemsException { - final var result = new CompletableFuture(); - ModbusUtils.readELementOnce(this.modbusProtocol, new UnsignedWordElement(0), true).thenAccept(value -> { - result.complete(isHashEqual(value, "OpenEMS")); - }); - return result; + private CompletableFuture isOpenems() { + return readElementOnce(this.modbusProtocol, ModbusUtils::retryOnNull, new UnsignedWordElement(0)) // + .thenCompose(value -> completedFuture(isHashEqual(value, "OpenEMS"))); } /** @@ -166,55 +161,41 @@ private CompletableFuture findComponentBlock(String componentId, int st } private void _findComponentBlock(CompletableFuture result, String componentId, int startAddress) { - try { - ModbusUtils.readELementOnce(this.modbusProtocol, new StringWordElement(startAddress, 16), false) - .thenAccept(remoteComponentId -> { - if (remoteComponentId == null) { - result.completeExceptionally( - new OpenemsException("Unable to find remote Component with ID " + componentId)); - } - if (remoteComponentId.equals(componentId)) { - this.logInfo(this.log, - "Found Remote-Component '" + componentId + "' on address " + startAddress); - result.complete(startAddress); - return; - } - try { - ModbusUtils.readELementOnce(this.modbusProtocol, new UnsignedWordElement(startAddress + 16), - false).thenAccept(lengthOfBlock -> { - this._findComponentBlock(result, componentId, startAddress + lengthOfBlock); - }); - } catch (OpenemsException e) { - result.completeExceptionally(e); - } - }); - } catch (OpenemsException e) { - result.completeExceptionally(e); - } + readElementOnce(this.modbusProtocol, ModbusUtils::retryOnNull, new StringWordElement(startAddress, 16)) // + .thenAccept(remoteComponentId -> { + if (remoteComponentId == null) { + result.completeExceptionally( + new OpenemsException("Unable to find remote Component with ID " + componentId)); + } + if (remoteComponentId.equals(componentId)) { + this.logInfo(this.log, + "Found Remote-Component '" + componentId + "' on address " + startAddress); + result.complete(startAddress); + return; + } + readElementOnce(this.modbusProtocol, ModbusUtils::retryOnNull, + new UnsignedWordElement(startAddress + 16)) // + .thenAccept(lengthOfBlock -> { + this._findComponentBlock(result, componentId, startAddress + lengthOfBlock); + }); + }); } private CompletableFuture readNatureBlocks(int startAddress) { - final var result = new CompletableFuture(); - try { - ModbusUtils.readELementOnce(this.modbusProtocol, new UnsignedWordElement(startAddress + 16), false) - .thenAccept(lengthOfComponentBlock -> { - var lastAddress = startAddress + lengthOfComponentBlock + 20; - // TODO fix length of last component blocks in Slave Modbus/TCP-Api - this.readNatureStartAddresses(startAddress + 20, lastAddress) - .thenAccept(natureStartAddresses -> { - try { - this.mapRemoteChannels(natureStartAddresses); - result.complete(null); - - } catch (OpenemsException e) { - result.completeExceptionally(e); - } - }); - }); - } catch (OpenemsException e) { - result.completeExceptionally(e); - } - return result; + return readElementOnce(this.modbusProtocol, ModbusUtils::doNotRetry, new UnsignedWordElement(startAddress + 16)) + .thenCompose(lengthOfComponentBlock -> + // TODO fix length of last component blocks in Slave Modbus/TCP-Api + this.readNatureStartAddresses(startAddress + 20, + startAddress + lengthOfComponentBlock + 20 /* last address */) + .thenCompose(natureStartAddresses -> { + try { + this.mapRemoteChannels(natureStartAddresses); + return completedFuture(null); + + } catch (OpenemsException e) { + return CompletableFuture.failedFuture(e); + } + })); } /** @@ -438,40 +419,31 @@ private CompletableFuture> readNatureStartAddresses(int private void _readNatureStartAddresses(CompletableFuture> result, int startAddress, int lastAddress, final TreeMap natureStartAddresses) { - try { - ModbusUtils.readELementOnce(this.modbusProtocol, new UnsignedWordElement(startAddress), false) - .thenAccept(rawHash -> { - if (rawHash == null) { - result.completeExceptionally( - new OpenemsException("Unable to read hash at " + startAddress)); - return; - } - var hash = (short) (int) rawHash; - - try { - ModbusUtils.readELementOnce(this.modbusProtocol, new UnsignedWordElement(startAddress + 1), - false).thenAccept(lengthOfNatureBlock -> { - this.logInfo(this.log, "Found Remote-Nature '0x" - + Integer.toHexString(hash & 0xffff) + "' on address " + startAddress); - // TODO get Remote-Nature name from this.modbusSlaveNatureTableMethods - natureStartAddresses.put(startAddress, hash); - - var nextStartAddress = startAddress + lengthOfNatureBlock; - if (nextStartAddress >= lastAddress) { - result.complete(natureStartAddresses); - - } else { - // recursive call of _readNatureStartAddresses - this._readNatureStartAddresses(result, nextStartAddress, lastAddress, - natureStartAddresses); - } - }); - } catch (OpenemsException e) { - result.completeExceptionally(e); - } - }); - } catch (OpenemsException e) { - result.completeExceptionally(e); - } + readElementOnce(this.modbusProtocol, ModbusUtils::retryOnNull, new UnsignedWordElement(startAddress)) + .thenAccept(rawHash -> { + if (rawHash == null) { + result.completeExceptionally(new OpenemsException("Unable to read hash at " + startAddress)); + return; + } + var hash = (short) (int) rawHash; + + readElementOnce(this.modbusProtocol, ModbusUtils::doNotRetry, + new UnsignedWordElement(startAddress + 1)).thenAccept(lengthOfNatureBlock -> { + this.logInfo(this.log, "Found Remote-Nature '0x" + Integer.toHexString(hash & 0xffff) + + "' on address " + startAddress); + // TODO get Remote-Nature name from this.modbusSlaveNatureTableMethods + natureStartAddresses.put(startAddress, hash); + + var nextStartAddress = startAddress + lengthOfNatureBlock; + if (nextStartAddress >= lastAddress) { + result.complete(natureStartAddresses); + + } else { + // recursive call of _readNatureStartAddresses + this._readNatureStartAddresses(result, nextStartAddress, lastAddress, + natureStartAddresses); + } + }); + }); } } diff --git a/io.openems.edge.edge2edge/src/io/openems/edge/edge2edge/ess/Edge2EdgeEssImpl.java b/io.openems.edge.edge2edge/src/io/openems/edge/edge2edge/ess/Edge2EdgeEssImpl.java index 5c816d12c97..3273b2127fd 100644 --- a/io.openems.edge.edge2edge/src/io/openems/edge/edge2edge/ess/Edge2EdgeEssImpl.java +++ b/io.openems.edge.edge2edge/src/io/openems/edge/edge2edge/ess/Edge2EdgeEssImpl.java @@ -54,7 +54,7 @@ protected void setModbus(BridgeModbus modbus) { super.setModbus(modbus); } - public Edge2EdgeEssImpl() throws OpenemsException { + public Edge2EdgeEssImpl() { super(// Lists.newArrayList(// OpenemsComponent::getModbusSlaveNatureTable, // diff --git a/io.openems.edge.meter.microcare.sdm630/.classpath b/io.openems.edge.energy.api/.classpath similarity index 100% rename from io.openems.edge.meter.microcare.sdm630/.classpath rename to io.openems.edge.energy.api/.classpath diff --git a/io.openems.edge.energy.api/.gitignore b/io.openems.edge.energy.api/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.edge.energy.api/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.edge.meter.microcare.sdm630/.project b/io.openems.edge.energy.api/.project similarity index 90% rename from io.openems.edge.meter.microcare.sdm630/.project rename to io.openems.edge.energy.api/.project index 3694691cf2b..edc8917776f 100644 --- a/io.openems.edge.meter.microcare.sdm630/.project +++ b/io.openems.edge.energy.api/.project @@ -1,6 +1,6 @@ - io.openems.edge.meter.microcare.sdm630 + io.openems.edge.energy.api diff --git a/io.openems.edge.meter.microcare.sdm630/.settings/org.eclipse.core.resources.prefs b/io.openems.edge.energy.api/.settings/org.eclipse.core.resources.prefs similarity index 100% rename from io.openems.edge.meter.microcare.sdm630/.settings/org.eclipse.core.resources.prefs rename to io.openems.edge.energy.api/.settings/org.eclipse.core.resources.prefs diff --git a/io.openems.edge.energy.api/bnd.bnd b/io.openems.edge.energy.api/bnd.bnd new file mode 100644 index 00000000000..49a30e8f94b --- /dev/null +++ b/io.openems.edge.energy.api/bnd.bnd @@ -0,0 +1,15 @@ +Bundle-Name: OpenEMS Edge Energy Api +Bundle-Vendor: FENECON GmbH +Bundle-License: https://opensource.org/licenses/EPL-2.0 +Bundle-Version: 1.0.0.${tstamp} + +-buildpath: \ + ${buildpath},\ + io.openems.common,\ + io.openems.edge.common,\ + io.openems.edge.controller.api,\ + io.openems.edge.predictor.api,\ + io.openems.edge.timeofusetariff.api,\ + +-testpath: \ + ${testpath},\ diff --git a/io.openems.edge.energy.api/readme.adoc b/io.openems.edge.energy.api/readme.adoc new file mode 100644 index 00000000000..48c7bd7b577 --- /dev/null +++ b/io.openems.edge.energy.api/readme.adoc @@ -0,0 +1,5 @@ += OpenEMS Energy API + +The API for OpenEMS Energy Schedules. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.energy.api[Source Code icon:github[]] \ No newline at end of file diff --git a/io.openems.edge.energy.api/src/io/openems/edge/energy/api/EnergySchedulable.java b/io.openems.edge.energy.api/src/io/openems/edge/energy/api/EnergySchedulable.java new file mode 100644 index 00000000000..cf2f7e66e0d --- /dev/null +++ b/io.openems.edge.energy.api/src/io/openems/edge/energy/api/EnergySchedulable.java @@ -0,0 +1,13 @@ +package io.openems.edge.energy.api; + +import io.openems.edge.controller.api.Controller; + +public interface EnergySchedulable extends Controller { + + /** + * Get the {@link EnergyScheduleHandler}. + * + * @return {@link EnergyScheduleHandler} + */ + public EnergyScheduleHandler getEnergyScheduleHandler(); +} diff --git a/io.openems.edge.energy.api/src/io/openems/edge/energy/api/EnergyScheduleHandler.java b/io.openems.edge.energy.api/src/io/openems/edge/energy/api/EnergyScheduleHandler.java new file mode 100644 index 00000000000..ca5ca2ebc45 --- /dev/null +++ b/io.openems.edge.energy.api/src/io/openems/edge/energy/api/EnergyScheduleHandler.java @@ -0,0 +1,76 @@ +package io.openems.edge.energy.api; + +import static io.openems.common.utils.DateUtils.roundDownToQuarter; + +import java.time.ZonedDateTime; +import java.util.Optional; +import java.util.function.Supplier; + +import com.google.common.collect.ImmutableMap; + +public class EnergyScheduleHandler { + + private final Supplier availableStates; + private final Supplier context; + + private ImmutableMap> schedule = ImmutableMap.of(); + + public EnergyScheduleHandler(Supplier availableStates, Supplier context) { + this.availableStates = availableStates; + this.context = context; + } + + /** + * Gets the available States. + * + * @return an Array of States + */ + public STATE[] getAvailableStates() { + return this.availableStates.get(); + } + + /** + * Gets the Context. + * + * @return the Context + */ + public CONTEXT getContext() { + return this.context.get(); + } + + public static record Period(STATE state, Integer essChargeInChargeGrid) { + } + + /** + * Sets the Schedule. Called by Optimizer. + * + * @param schedule the Schedule + */ + public synchronized void setSchedule(ImmutableMap> schedule) { + this.schedule = schedule; + } + + /** + * Gets the current State or null. + * + * @return the State or null + */ + public synchronized STATE getCurrentState() { + return Optional.ofNullable(this.schedule.get(roundDownToQuarter(ZonedDateTime.now()))) // + .map(Period::state) // + .orElse(null); + } + + // TODO hacky... find a better way! + /** + * Gets the current essChargeInChargeGrid or null. + * + * @return the essChargeInChargeGrid or null + */ + public synchronized Integer getCurrentEssChargeInChargeGrid() { + return Optional.ofNullable(this.schedule.get(roundDownToQuarter(ZonedDateTime.now()))) // + .map(Period::essChargeInChargeGrid) // + .orElse(null); + } + +} diff --git a/io.openems.edge.energy.api/src/io/openems/edge/energy/api/EnergyScheduler.java b/io.openems.edge.energy.api/src/io/openems/edge/energy/api/EnergyScheduler.java new file mode 100644 index 00000000000..7d58264b26b --- /dev/null +++ b/io.openems.edge.energy.api/src/io/openems/edge/energy/api/EnergyScheduler.java @@ -0,0 +1,29 @@ +package io.openems.edge.energy.api; + +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.jsonapi.ComponentJsonApi; + +/** + * The global Energy Schedule optimizer singleton. + */ +public interface EnergyScheduler extends OpenemsComponent, ComponentJsonApi { + + public static final String SINGLETON_SERVICE_PID = "Core.Energy"; + public static final String SINGLETON_COMPONENT_ID = "_energy"; + + public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + ; + + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + @Override + public Doc doc() { + return this.doc; + } + } +} diff --git a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/package-info.java b/io.openems.edge.energy.api/src/io/openems/edge/energy/api/package-info.java similarity index 63% rename from io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/package-info.java rename to io.openems.edge.energy.api/src/io/openems/edge/energy/api/package-info.java index cd7e2d65c23..a842873a538 100644 --- a/io.openems.edge.controller.api.backend/src/io/openems/edge/controller/api/backend/package-info.java +++ b/io.openems.edge.energy.api/src/io/openems/edge/energy/api/package-info.java @@ -1,3 +1,3 @@ @org.osgi.annotation.versioning.Version("1.0.0") @org.osgi.annotation.bundle.Export -package io.openems.edge.controller.api.backend; +package io.openems.edge.energy.api; diff --git a/io.openems.edge.meter.microcare.sdm630/test/.gitignore b/io.openems.edge.energy.api/test/.gitignore similarity index 100% rename from io.openems.edge.meter.microcare.sdm630/test/.gitignore rename to io.openems.edge.energy.api/test/.gitignore diff --git a/io.openems.edge.energy/.classpath b/io.openems.edge.energy/.classpath new file mode 100644 index 00000000000..bbfbdbe40e7 --- /dev/null +++ b/io.openems.edge.energy/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/io.openems.edge.energy/.gitignore b/io.openems.edge.energy/.gitignore new file mode 100644 index 00000000000..90dde36e4ac --- /dev/null +++ b/io.openems.edge.energy/.gitignore @@ -0,0 +1,3 @@ +/bin/ +/bin_test/ +/generated/ diff --git a/io.openems.edge.energy/.project b/io.openems.edge.energy/.project new file mode 100644 index 00000000000..6126f1ae321 --- /dev/null +++ b/io.openems.edge.energy/.project @@ -0,0 +1,23 @@ + + + io.openems.edge.energy + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.edge.energy/.settings/org.eclipse.core.resources.prefs b/io.openems.edge.energy/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..99f26c0203a --- /dev/null +++ b/io.openems.edge.energy/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/io.openems.edge.energy/bnd.bnd b/io.openems.edge.energy/bnd.bnd new file mode 100644 index 00000000000..7df50f07c5e --- /dev/null +++ b/io.openems.edge.energy/bnd.bnd @@ -0,0 +1,22 @@ +Bundle-Name: OpenEMS Edge Energy +Bundle-Vendor: FENECON GmbH +Bundle-License: https://opensource.org/licenses/EPL-2.0 +Bundle-Version: 1.0.0.${tstamp} + +-buildpath: \ + ${buildpath},\ + io.openems.common,\ + io.openems.edge.common,\ + io.openems.edge.controller.api,\ + io.openems.edge.controller.ess.emergencycapacityreserve,\ + io.openems.edge.controller.ess.limittotaldischarge,\ + io.openems.edge.controller.ess.timeofusetariff,\ + io.openems.edge.energy.api,\ + io.openems.edge.ess.api,\ + io.openems.edge.predictor.api,\ + io.openems.edge.timedata.api,\ + io.openems.edge.timeofusetariff.api,\ + io.openems.wrapper.jenetics,\ + +-testpath: \ + ${testpath} \ No newline at end of file diff --git a/io.openems.edge.energy/readme.adoc b/io.openems.edge.energy/readme.adoc new file mode 100644 index 00000000000..e0c1789dd4d --- /dev/null +++ b/io.openems.edge.energy/readme.adoc @@ -0,0 +1,5 @@ += OpenEMS Energy + +Implementations and Services for OpenEMS Energy Schedules. + +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.energy[Source Code icon:github[]] \ No newline at end of file diff --git a/io.openems.edge.energy/src/io/openems/edge/energy/Config.java b/io.openems.edge.energy/src/io/openems/edge/energy/Config.java new file mode 100644 index 00000000000..5b1344d5a72 --- /dev/null +++ b/io.openems.edge.energy/src/io/openems/edge/energy/Config.java @@ -0,0 +1,15 @@ +package io.openems.edge.energy; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@ObjectClassDefinition(// + name = "Core Energy Scheduler", // + description = "The global Energy Scheduler.") +@interface Config { + + @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") + boolean enabled() default true; + + String webconsole_configurationFactory_nameHint() default "Core Energy Scheduler"; +} \ No newline at end of file diff --git a/io.openems.edge.energy/src/io/openems/edge/energy/EnergySchedulerImpl.java b/io.openems.edge.energy/src/io/openems/edge/energy/EnergySchedulerImpl.java new file mode 100644 index 00000000000..1f0e892b447 --- /dev/null +++ b/io.openems.edge.energy/src/io/openems/edge/energy/EnergySchedulerImpl.java @@ -0,0 +1,149 @@ +package io.openems.edge.energy; + +import static io.openems.edge.energy.optimizer.Utils.handleGetScheduleRequest; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Modified; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.metatype.annotations.Designate; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.common.component.AbstractOpenemsComponent; +import io.openems.edge.common.component.ComponentManager; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.JsonApiBuilder; +import io.openems.edge.common.sum.Sum; +import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImpl; +import io.openems.edge.energy.api.EnergySchedulable; +import io.openems.edge.energy.api.EnergyScheduler; +import io.openems.edge.energy.jsonrpc.GetScheduleRequest; +import io.openems.edge.energy.optimizer.GlobalContext; +import io.openems.edge.energy.optimizer.Optimizer; +import io.openems.edge.predictor.api.manager.PredictorManager; +import io.openems.edge.timedata.api.Timedata; +import io.openems.edge.timeofusetariff.api.TimeOfUseTariff; + +@Designate(ocd = Config.class, factory = false) +@Component(// + name = EnergyScheduler.SINGLETON_SERVICE_PID, // + immediate = true, // + configurationPolicy = ConfigurationPolicy.OPTIONAL // +) +public class EnergySchedulerImpl extends AbstractOpenemsComponent + implements OpenemsComponent, EnergyScheduler, ComponentJsonApi { + + /** The hard working Optimizer. */ + private final Optimizer optimizer; + + @Reference + private ConfigurationAdmin cm; + + @Reference + private ComponentManager componentManager; + + @Reference + private PredictorManager predictorManager; + + @Reference(policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) + private volatile TimeOfUseTariff timeOfUseTariff; + + @Reference + private Sum sum; + + @Reference(policyOption = ReferencePolicyOption.GREEDY, // + cardinality = ReferenceCardinality.MULTIPLE, // + policy = ReferencePolicy.DYNAMIC, // + target = "(enabled=true)") + private volatile List> schedulables = new CopyOnWriteArrayList<>(); + + @Reference(policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) + private volatile Timedata timedata; + + public EnergySchedulerImpl() { + super(// + OpenemsComponent.ChannelId.values(), // + EnergyScheduler.ChannelId.values() // + ); + // Prepare Optimizer and Context + this.optimizer = new Optimizer(() -> { + if (this.timeOfUseTariff == null) { + throw new OpenemsException("TimeOfUseTariff is not available"); + } + var ctrl = this.schedulables.stream() // + .filter(TimeOfUseTariffControllerImpl.class::isInstance) // + .map(TimeOfUseTariffControllerImpl.class::cast) // + .findFirst().orElse(null); + if (ctrl == null) { + throw new OpenemsException("TimeOfUseTariffController is not available"); + } + var esh = ctrl.getEnergyScheduleHandler(); + // NOTE: This is a workaround while we refactor TimeOfUseTariffController + // This code assumes that the `EnergySchedulable` is a + // `TimeOfUseTariffController` + return GlobalContext.create() // + .setClock(this.componentManager.getClock()) // + .setEnergyScheduleHandler(esh) // + .setSum(this.sum) // + .setPredictorManager(this.predictorManager) // + .setTimeOfUseTariff(this.timeOfUseTariff) // + .build(); + }); + } + + @Activate + private void activate(ComponentContext context, Config config) throws OpenemsException { + super.activate(context, SINGLETON_COMPONENT_ID, SINGLETON_SERVICE_PID, true); + if (this.applyConfig(config)) { + this.optimizer.activate(this.id()); + } + } + + @Modified + private void modified(ComponentContext context, Config config) throws OpenemsNamedException { + super.modified(context, SINGLETON_COMPONENT_ID, SINGLETON_SERVICE_PID, true); + if (this.applyConfig(config)) { + this.optimizer.modified(this.id()); + } + } + + private synchronized boolean applyConfig(Config config) { + if (OpenemsComponent.validateSingleton(this.cm, SINGLETON_SERVICE_PID, SINGLETON_COMPONENT_ID)) { + return false; + } + + if (!config.enabled()) { + this.optimizer.deactivate(); + return false; + } + + return true; + } + + @Override + @Deactivate + protected void deactivate() { + this.optimizer.deactivate(); + super.deactivate(); + } + + @Override + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(GetScheduleRequest.METHOD, call -> handleGetScheduleRequest(// + this.optimizer, call.getRequest().getId(), this.timedata, this.timeOfUseTariff, + "ctrlEssTimeOfUseTariff0", ZonedDateTime.now(this.componentManager.getClock()))); + } +} diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/GetScheduleRequest.java b/io.openems.edge.energy/src/io/openems/edge/energy/jsonrpc/GetScheduleRequest.java similarity index 94% rename from io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/GetScheduleRequest.java rename to io.openems.edge.energy/src/io/openems/edge/energy/jsonrpc/GetScheduleRequest.java index ff357bfd827..1dbdb0dc7d8 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/GetScheduleRequest.java +++ b/io.openems.edge.energy/src/io/openems/edge/energy/jsonrpc/GetScheduleRequest.java @@ -1,4 +1,4 @@ -package io.openems.edge.controller.ess.timeofusetariff.jsonrpc; +package io.openems.edge.energy.jsonrpc; import com.google.gson.JsonObject; diff --git a/io.openems.edge.energy/src/io/openems/edge/energy/jsonrpc/GetScheduleResponse.java b/io.openems.edge.energy/src/io/openems/edge/energy/jsonrpc/GetScheduleResponse.java new file mode 100644 index 00000000000..e27461abc45 --- /dev/null +++ b/io.openems.edge.energy/src/io/openems/edge/energy/jsonrpc/GetScheduleResponse.java @@ -0,0 +1,73 @@ +package io.openems.edge.energy.jsonrpc; + +import java.time.ZonedDateTime; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.UUID; + +import com.google.gson.JsonObject; + +import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; +import io.openems.common.utils.JsonUtils; +import io.openems.edge.energy.optimizer.ScheduleDatas; +import io.openems.edge.energy.optimizer.ScheduleDatas.ScheduleData; + +/** + * Represents a JSON-RPC Response for 'getMeters'. + * + *
+ * {
+ *   "jsonrpc": "2.0",
+ *   "id": "UUID",
+ *   "result": {
+ *     'schedule': [{
+ *      'timestamp':...,
+ *      'price':...,
+ *      'state':...,
+ *      'grid':...,
+ *      'production':...,
+ *      'consumption':...,
+ *      'ess':...,
+ *      'soc':...,
+ *     }]
+ *   }
+ * }
+ * 
+ */ +public class GetScheduleResponse extends JsonrpcResponseSuccess { + + private final ZonedDateTime fromDate; + private final ZonedDateTime toDate; + private final ScheduleDatas scheduleDatas; + + public GetScheduleResponse(ZonedDateTime fromDate, ZonedDateTime toDate, ScheduleDatas scheduleDatas) { + this(UUID.randomUUID(), fromDate, toDate, scheduleDatas); + } + + public GetScheduleResponse(UUID id, ZonedDateTime fromDate, ZonedDateTime toDate, ScheduleDatas scheduleDatas) { + super(id); + this.fromDate = fromDate; + this.toDate = toDate; + this.scheduleDatas = scheduleDatas; + } + + @Override + public JsonObject getResult() { + var s = new TreeMap(); + var t = this.fromDate; + // Prefill with empty objects + while (!t.isAfter(this.toDate)) { + s.put(t, ScheduleData.emptyJsonObject(t)); + t = t.plusMinutes(15); + } + // Replace with actual data + s.putAll(this.scheduleDatas.toJsonObjects()); + + return JsonUtils.buildJsonObject() // + .add("schedule", s.entrySet().stream() // + .map(Entry::getValue) // + .collect(JsonUtils.toJsonArray())) // + .build(); + } + +} diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/EnergyFlow.java b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/EnergyFlow.java similarity index 96% rename from io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/EnergyFlow.java rename to io.openems.edge.energy/src/io/openems/edge/energy/optimizer/EnergyFlow.java index 0f43d3cca58..bf108379c74 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/EnergyFlow.java +++ b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/EnergyFlow.java @@ -1,11 +1,11 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; import static io.openems.edge.common.type.TypeUtils.fitWithin; import static java.lang.Math.max; import static java.lang.Math.min; import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.OptimizePeriod; +import io.openems.edge.energy.optimizer.Params.OptimizePeriod; /** * Simulates a detailed Energy-Flow. diff --git a/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/GlobalContext.java b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/GlobalContext.java new file mode 100644 index 00000000000..c7b889a6eff --- /dev/null +++ b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/GlobalContext.java @@ -0,0 +1,103 @@ +package io.openems.edge.energy.optimizer; + +import java.time.Clock; + +import io.openems.edge.common.sum.Sum; +import io.openems.edge.controller.ess.timeofusetariff.StateMachine; +import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffController; +import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImpl; +import io.openems.edge.energy.api.EnergyScheduleHandler; +import io.openems.edge.predictor.api.manager.PredictorManager; +import io.openems.edge.timeofusetariff.api.TimeOfUseTariff; + +public record GlobalContext(// + Clock clock, // + EnergyScheduleHandler energyScheduleHandler, // + Sum sum, // + PredictorManager predictorManager, // + TimeOfUseTariff timeOfUseTariff) { + + public static class Builder { + private Clock clock; + private EnergyScheduleHandler energyScheduleHandler; + private Sum sum; + private PredictorManager predictorManager; + private TimeOfUseTariff timeOfUseTariff; + + /** + * The {@link Clock}. + * + * @param clock the {@link Clock} + * @return myself + */ + public Builder setClock(Clock clock) { + this.clock = clock; + return this; + } + + /** + * The {@link EnergyScheduleHandler} of the {@link TimeOfUseTariffController}. + * + * @param energyScheduleHandler the {@link EnergyScheduleHandler} + * @return myself + */ + public Builder setEnergyScheduleHandler( + EnergyScheduleHandler energyScheduleHandler) { + this.energyScheduleHandler = energyScheduleHandler; + return this; + } + + /** + * The {@link Sum}. + * + * @param sum the {@link Sum} + * @return myself + */ + public Builder setSum(Sum sum) { + this.sum = sum; + return this; + } + + /** + * The {@link PredictorManager}. + * + * @param predictorManager the {@link PredictorManager} + * @return myself + */ + public Builder setPredictorManager(PredictorManager predictorManager) { + this.predictorManager = predictorManager; + return this; + } + + /** + * The {@link TimeOfUseTariff}. + * + * @param timeOfUseTariff the {@link TimeOfUseTariff} + * @return myself + */ + public Builder setTimeOfUseTariff(TimeOfUseTariff timeOfUseTariff) { + this.timeOfUseTariff = timeOfUseTariff; + return this; + } + + /** + * Builds the {@link GlobalContext}. + * + * @return the {@link GlobalContext} record + */ + public GlobalContext build() { + return new GlobalContext(this.clock, this.energyScheduleHandler, this.sum, this.predictorManager, + this.timeOfUseTariff); + } + } + + /** + * Create a {@link GlobalContext} {@link Builder}. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new GlobalContext.Builder(); + } + +} diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/InitialPopulationUtils.java b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/InitialPopulationUtils.java similarity index 92% rename from io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/InitialPopulationUtils.java rename to io.openems.edge.energy/src/io/openems/edge/energy/optimizer/InitialPopulationUtils.java index 40a7dbc0901..b09e3e3036c 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/InitialPopulationUtils.java +++ b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/InitialPopulationUtils.java @@ -1,10 +1,10 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.findFirstPeakIndex; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.findFirstValleyIndex; +import static io.openems.edge.energy.optimizer.Utils.findFirstPeakIndex; +import static io.openems.edge.energy.optimizer.Utils.findFirstValleyIndex; import java.util.Arrays; import java.util.List; @@ -19,7 +19,7 @@ import io.jenetics.IntegerChromosome; import io.jenetics.IntegerGene; import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.OptimizePeriod; +import io.openems.edge.energy.optimizer.Params.OptimizePeriod; public class InitialPopulationUtils { diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Optimizer.java b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Optimizer.java similarity index 58% rename from io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Optimizer.java rename to io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Optimizer.java index 693f11a82a6..030b0c3e4dc 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Optimizer.java +++ b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Optimizer.java @@ -1,13 +1,13 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; +import static com.google.common.collect.ImmutableMap.toImmutableMap; import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap; -import static io.openems.common.utils.DateUtils.roundDownToQuarter; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.simulate; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.calculateExecutionLimitSeconds; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.createSimulatorParams; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.initializeRandomRegistryForProduction; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.logSchedule; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.updateSchedule; +import static io.openems.edge.energy.optimizer.Simulator.simulate; +import static io.openems.edge.energy.optimizer.Utils.calculateExecutionLimitSeconds; +import static io.openems.edge.energy.optimizer.Utils.createSimulatorParams; +import static io.openems.edge.energy.optimizer.Utils.initializeRandomRegistryForProduction; +import static io.openems.edge.energy.optimizer.Utils.logSchedule; +import static io.openems.edge.energy.optimizer.Utils.updateSchedule; import static java.lang.Thread.sleep; import java.time.Duration; @@ -15,18 +15,18 @@ import java.time.ZonedDateTime; import java.util.Map.Entry; import java.util.TreeMap; -import java.util.function.Supplier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableSortedMap; -import io.openems.common.exceptions.InvalidValueException; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.function.ThrowingSupplier; import io.openems.common.test.TimeLeapClock; import io.openems.common.worker.AbstractImmediateWorker; -import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.Period; +import io.openems.edge.energy.api.EnergyScheduleHandler; +import io.openems.edge.energy.optimizer.Simulator.Period; /** * This task is executed once in the beginning and afterwards every full 15 @@ -36,13 +36,13 @@ public class Optimizer extends AbstractImmediateWorker { private final Logger log = LoggerFactory.getLogger(Optimizer.class); - private final Supplier context; + private final ThrowingSupplier globalContext; private final TreeMap schedule = new TreeMap<>(); private Params params = null; - public Optimizer(Supplier context) { - this.context = context; + public Optimizer(ThrowingSupplier globalContext) { + this.globalContext = globalContext; initializeRandomRegistryForProduction(); // Run Optimizer thread in LOW PRIORITY @@ -50,18 +50,18 @@ public Optimizer(Supplier context) { } @Override - public void forever() throws InterruptedException { + public void forever() throws InterruptedException, OpenemsException { this.log.info("# Start next run of Optimizer"); this.createParams(); // this possibly takes forever - final var context = this.context.get(); - final var start = Instant.now(context.clock()); + final var globalContext = this.globalContext.get(); + final var start = Instant.now(globalContext.clock()); long executionLimitSeconds; // Calculate max execution time till next quarter (with buffer) - executionLimitSeconds = calculateExecutionLimitSeconds(context.clock()); + executionLimitSeconds = calculateExecutionLimitSeconds(globalContext.clock()); // Find best Schedule var schedule = Simulator.getBestSchedule(this.params, executionLimitSeconds); @@ -74,13 +74,20 @@ public void forever() throws InterruptedException { // Update Schedule from newly simulated Schedule synchronized (this.schedule) { - updateSchedule(ZonedDateTime.now(context.clock()), this.schedule, newSchedule); + updateSchedule(ZonedDateTime.now(globalContext.clock()), this.schedule, newSchedule); } + // Send Schedule to Controller + globalContext.energyScheduleHandler().setSchedule(this.schedule.entrySet().stream()// + .collect(toImmutableMap(// + Entry::getKey, // + e -> new EnergyScheduleHandler.Period<>(e.getValue().state(), + e.getValue().op().essChargeInChargeGrid())))); + // Sleep remaining time - if (!(context.clock() instanceof TimeLeapClock)) { + if (!(globalContext.clock() instanceof TimeLeapClock)) { var remainingExecutionLimit = Duration - .between(Instant.now(context.clock()), start.plusSeconds(executionLimitSeconds)).getSeconds(); + .between(Instant.now(globalContext.clock()), start.plusSeconds(executionLimitSeconds)).getSeconds(); if (remainingExecutionLimit > 0) { this.log.info("Sleep [" + remainingExecutionLimit + "s] till next run of Optimizer"); sleep(remainingExecutionLimit * 1000); @@ -97,7 +104,7 @@ private void createParams() throws InterruptedException { while (true) { try { synchronized (this.schedule) { - this.params = createSimulatorParams(this.context.get(), // + this.params = createSimulatorParams(this.globalContext.get(), // this.schedule.entrySet().stream() // .collect(toImmutableSortedMap(// ZonedDateTime::compareTo, // @@ -105,7 +112,7 @@ private void createParams() throws InterruptedException { return; } - } catch (InvalidValueException e) { + } catch (OpenemsException e) { this.log.info("# Stuck trying to get Params. " + e.getMessage()); this.params = null; synchronized (this.schedule) { @@ -125,21 +132,6 @@ public Params getParams() { return this.params; } - /** - * Gets the current {@link StateMachine} or null. - * - * @return {@link StateMachine} or null - */ - public StateMachine getCurrentStateMachine() { - synchronized (this.schedule) { - var period = this.schedule.get(roundDownToQuarter(ZonedDateTime.now())); - if (period != null) { - return period.state(); - } - } - return null; - } - /** * Gets a copy of the Schedule. * diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Params.java b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Params.java similarity index 96% rename from io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Params.java rename to io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Params.java index 1513c1e44a7..b13de0b8546 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Params.java +++ b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Params.java @@ -1,8 +1,8 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; import static com.google.common.collect.ImmutableList.toImmutableList; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.ParamsUtils.calculateChargeEnergyInChargeGrid; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.ParamsUtils.calculatePeriodLengthHourFromIndex; +import static io.openems.edge.energy.optimizer.ParamsUtils.calculateChargeEnergyInChargeGrid; +import static io.openems.edge.energy.optimizer.ParamsUtils.calculatePeriodLengthHourFromIndex; import static java.lang.Math.min; import java.time.ZonedDateTime; diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/ParamsUtils.java b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/ParamsUtils.java similarity index 88% rename from io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/ParamsUtils.java rename to io.openems.edge.energy/src/io/openems/edge/energy/optimizer/ParamsUtils.java index b99381c6794..f350cf2ab44 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/ParamsUtils.java +++ b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/ParamsUtils.java @@ -1,10 +1,10 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; import static com.google.common.math.Quantiles.percentiles; import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffController.PERIODS_PER_HOUR; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.ESS_CHARGE_C_RATE; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.findFirstPeakIndex; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.findFirstValleyIndex; +import static io.openems.edge.energy.optimizer.Utils.ESS_CHARGE_C_RATE; +import static io.openems.edge.energy.optimizer.Utils.findFirstPeakIndex; +import static io.openems.edge.energy.optimizer.Utils.findFirstValleyIndex; import static java.lang.Math.max; import static java.lang.Math.min; import static java.lang.Math.round; @@ -16,7 +16,7 @@ import com.google.common.primitives.ImmutableIntArray; import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.Period; +import io.openems.edge.energy.optimizer.Simulator.Period; public class ParamsUtils { diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/ScheduleDatas.java b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/ScheduleDatas.java similarity index 78% rename from io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/ScheduleDatas.java rename to io.openems.edge.energy/src/io/openems/edge/energy/optimizer/ScheduleDatas.java index d2251fa011b..053dac39d13 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/ScheduleDatas.java +++ b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/ScheduleDatas.java @@ -1,16 +1,19 @@ -package io.openems.edge.controller.ess.timeofusetariff.jsonrpc; +package io.openems.edge.energy.optimizer; import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSortedMap.toImmutableSortedMap; import static io.openems.common.types.OptionsEnum.getOption; +import static io.openems.common.utils.JsonUtils.buildJsonObject; import static io.openems.common.utils.JsonUtils.getAsDouble; import static io.openems.common.utils.JsonUtils.getAsInt; import static io.openems.common.utils.JsonUtils.toJson; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_CONSUMPTION; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_ESS_DISCHARGE_POWER; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_ESS_SOC; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_GRID; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.SUM_PRODUCTION; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.toEnergy; +import static io.openems.edge.energy.optimizer.Utils.SUM_CONSUMPTION; +import static io.openems.edge.energy.optimizer.Utils.SUM_ESS_DISCHARGE_POWER; +import static io.openems.edge.energy.optimizer.Utils.SUM_ESS_SOC; +import static io.openems.edge.energy.optimizer.Utils.SUM_GRID; +import static io.openems.edge.energy.optimizer.Utils.SUM_PRODUCTION; +import static io.openems.edge.energy.optimizer.Utils.toEnergy; +import static io.openems.edge.energy.optimizer.Utils.toPower; import static java.lang.Double.parseDouble; import static java.lang.Integer.parseInt; import static java.lang.Math.round; @@ -32,18 +35,16 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSortedMap; -import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonNull; +import com.google.gson.JsonObject; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.ChannelAddress; -import io.openems.common.utils.JsonUtils; import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Optimizer; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.Length; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.Period; +import io.openems.edge.energy.optimizer.Params.Length; +import io.openems.edge.energy.optimizer.Simulator.Period; /** * Data for JSONRPC-Response. Values are in [W]. @@ -162,46 +163,16 @@ public String toLogString(String prefix) { } /** - * Builds a {@link JsonArray} of this {@link ScheduleDatas}. + * Builds a Map of {@link JsonObject}s of this {@link ScheduleDatas}. * - * @param fromDate the From-Date - * @return {@link JsonArray} + * @return a Map */ - public JsonArray toJsonArray(ZonedDateTime fromDate) { - var result = JsonUtils.buildJsonArray(); - var nextTime = fromDate; - - // Create the JSON object for each ScheduleData and add it to the schedule array - for (var e : this.entries) { - while (nextTime.isBefore(e.time())) { - // Add empty object for gaps - result.add(JsonUtils.buildJsonObject() // - .addProperty("timestamp", nextTime) // - .add("soc", JsonNull.INSTANCE) // - .add("production", JsonNull.INSTANCE) // - .add("consumption", JsonNull.INSTANCE) // - .add("state", JsonNull.INSTANCE) // - .add("price", JsonNull.INSTANCE) // - .add("ess", JsonNull.INSTANCE) // - .add("grid", JsonNull.INSTANCE) // - .build()); - nextTime = nextTime.plusMinutes(15); - } - - result.add(JsonUtils.buildJsonObject() // - .addProperty("timestamp", e.time()) // - .add("soc", toJson(round((e.essInitial() * 100) / (float) this.essTotalEnergy()))) // - .add("production", toJson(e.production())) // - .add("consumption", toJson(e.consumption())) // - .add("state", toJson(e.state.getValue())) // - .add("price", toJson(e.price())) // - .add("ess", toJson(e.essChargeDischarge())) // - .add("grid", toJson(e.grid())) // - .build()); - nextTime = e.time().plusMinutes(15); - } - - return result.build(); + public ImmutableSortedMap toJsonObjects() { + return this.entries().stream() // + .collect(toImmutableSortedMap(ZonedDateTime::compareTo, // + ScheduleData::time, // + sd -> sd.toJsonObject(this.essTotalEnergy()), // + (a, b) -> b)); } public record ScheduleData(// @@ -278,10 +249,47 @@ public static Stream fromHistoricDataQuery(int essTotalEnergy, } /** - * Creates a Stream of {@link ScheduleData}. + * Convert this {@link ScheduleData} to a {@link JsonObject}. * * @param essTotalEnergy ESS Total Energy (Capacity) [Wh] - * @param period the {@link Period} + * @return a JsonObject + */ + public JsonObject toJsonObject(int essTotalEnergy) { + return buildJsonObject() // + .addProperty("timestamp", this.time()) // + .add("soc", toJson(round((this.essInitial() * 100) / (float) essTotalEnergy))) // + .add("production", toJson(toPower(this.production()))) // + .add("consumption", toJson(toPower(this.consumption()))) // + .add("state", toJson(this.state.getValue())) // + .add("price", toJson(this.price())) // + .add("ess", toJson(toPower(this.essChargeDischarge()))) // + .add("grid", toJson(toPower(this.grid()))) // + .build(); + } + + /** + * Convert this {@link ScheduleData} to a {@link JsonObject}. + * + * @param essTotalEnergy ESS Total Energy (Capacity) [Wh] + * @return a JsonObject + */ + public static JsonObject emptyJsonObject(ZonedDateTime timestamp) { + return buildJsonObject() // + .addProperty("timestamp", timestamp) // + .add("soc", JsonNull.INSTANCE) // + .add("production", JsonNull.INSTANCE) // + .add("consumption", JsonNull.INSTANCE) // + .add("state", JsonNull.INSTANCE) // + .add("price", JsonNull.INSTANCE) // + .add("ess", JsonNull.INSTANCE) // + .add("grid", JsonNull.INSTANCE) // + .build(); + } + + /** + * Creates a Stream of {@link ScheduleData}. + * + * @param period the {@link Period} * @return a Stream of {@link ScheduleData} */ public static Stream fromPeriod(Period period) { diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Simulator.java b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Simulator.java similarity index 88% rename from io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Simulator.java rename to io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Simulator.java index 849e7cd3a11..0b79a5f6cc2 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Simulator.java +++ b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Simulator.java @@ -1,10 +1,10 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; import static io.jenetics.engine.EvolutionResult.toBestGenotype; import static io.jenetics.engine.Limits.byExecutionTime; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.InitialPopulationUtils.buildInitialPopulation; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.paramsAreValid; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.postprocessSimulatorState; +import static io.openems.edge.energy.optimizer.InitialPopulationUtils.buildInitialPopulation; +import static io.openems.edge.energy.optimizer.Utils.paramsAreValid; +import static io.openems.edge.energy.optimizer.Utils.postprocessSimulatorState; import static java.lang.Math.max; import static java.time.Duration.ofSeconds; @@ -24,8 +24,8 @@ import io.jenetics.engine.Engine; import io.jenetics.engine.EvolutionResult; import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.Length; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.OptimizePeriod; +import io.openems.edge.energy.optimizer.Params.Length; +import io.openems.edge.energy.optimizer.Params.OptimizePeriod; public class Simulator { @@ -105,17 +105,23 @@ protected static double simulatePeriod(Params p, OptimizePeriod op, StateMachine // Calculate Cost double cost; if (ef.grid() > 0) { + // Filter negative prices + var price = max(0, op.price()); + cost = // Cost for direct Consumption - ef.gridToConsumption() * op.price() + ef.gridToConsumption() * price // Cost for future Consumption after storage - + ef.gridToEss() * op.price() * EFFICIENCY_FACTOR; + + ef.gridToEss() * price * EFFICIENCY_FACTOR; } else { // Sell-to-Grid cost = 0.; } if (collect != null) { - var postprocessedState = postprocessSimulatorState(p, essInitial, state, ef); + var postprocessedState = postprocessSimulatorState(state, // + EnergyFlow.withBalancing(p, op, essInitial), // + EnergyFlow.withDelayDischarge(p, op, essInitial), // + EnergyFlow.withChargeGrid(p, op, essInitial)); collect.accept(new Period(op, postprocessedState, essInitial, ef)); } return cost; diff --git a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Utils.java b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Utils.java similarity index 66% rename from io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Utils.java rename to io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Utils.java index c3c07d1023a..4bc3d97858c 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/src/io/openems/edge/controller/ess/timeofusetariff/optimizer/Utils.java +++ b/io.openems.edge.energy/src/io/openems/edge/energy/optimizer/Utils.java @@ -1,16 +1,16 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; +import static com.google.common.collect.Streams.concat; import static io.openems.common.utils.DateUtils.roundDownToQuarter; import static io.openems.edge.common.type.TypeUtils.multiply; import static io.openems.edge.common.type.TypeUtils.orElse; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; import static io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffController.PERIODS_PER_HOUR; import static java.lang.Math.max; -import static java.lang.Math.min; import static java.lang.Math.round; import static java.util.Arrays.stream; -import static java.util.stream.IntStream.concat; import java.time.Clock; import java.time.Duration; @@ -34,23 +34,19 @@ import io.jenetics.util.RandomRegistry; import io.openems.common.exceptions.InvalidValueException; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; import io.openems.common.timedata.Resolution; import io.openems.common.types.ChannelAddress; -import io.openems.edge.common.sum.Sum; -import io.openems.edge.controller.api.Controller; import io.openems.edge.controller.ess.emergencycapacityreserve.ControllerEssEmergencyCapacityReserve; import io.openems.edge.controller.ess.limittotaldischarge.ControllerEssLimitTotalDischarge; import io.openems.edge.controller.ess.timeofusetariff.StateMachine; import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffController; -import io.openems.edge.controller.ess.timeofusetariff.jsonrpc.GetScheduleResponse; -import io.openems.edge.controller.ess.timeofusetariff.jsonrpc.ScheduleDatas; -import io.openems.edge.controller.ess.timeofusetariff.jsonrpc.ScheduleDatas.ScheduleData; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.Period; -import io.openems.edge.ess.api.HybridEss; -import io.openems.edge.ess.api.ManagedSymmetricEss; +import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImpl; +import io.openems.edge.energy.jsonrpc.GetScheduleResponse; +import io.openems.edge.energy.optimizer.ScheduleDatas.ScheduleData; +import io.openems.edge.energy.optimizer.Simulator.Period; import io.openems.edge.ess.api.SymmetricEss; import io.openems.edge.timedata.api.Timedata; +import io.openems.edge.timeofusetariff.api.TimeOfUseTariff; /** * Utils for {@link TimeOfUseTariffController}. @@ -127,28 +123,29 @@ private static void initializeRandomRegistry(boolean isUnitTest) { /** * Create {@link Params} for {@link Simulator}. * - * @param context the {@link Context} object + * @param globalContext the {@link GlobalContext} object * @param existingSchedule the existing schedule, i.e. result of previous * optimization * @return {@link Params} * @throws InvalidValueException on error */ - public static Params createSimulatorParams(Context context, + public static Params createSimulatorParams(GlobalContext globalContext, ImmutableSortedMap existingSchedule) throws InvalidValueException { final var time = roundDownToQuarter(ZonedDateTime.now()); // Prediction values final var predictionConsumption = joinConsumptionPredictions(4, // - context.predictorManager().getPrediction(SUM_CONSUMPTION).asArray(), // - context.predictorManager().getPrediction(SUM_UNMANAGED_CONSUMPTION).asArray()); + globalContext.predictorManager().getPrediction(SUM_CONSUMPTION).asArray(), // + globalContext.predictorManager().getPrediction(SUM_UNMANAGED_CONSUMPTION).asArray()); final var predictionProduction = generateProductionPrediction(// - context.predictorManager().getPrediction(SUM_PRODUCTION).asArray(), // + globalContext.predictorManager().getPrediction(SUM_PRODUCTION).asArray(), // predictionConsumption.length); // Prices contains the price values and the time it is retrieved. - final var prices = context.timeOfUseTariff().getPrices(); + final var prices = globalContext.timeOfUseTariff().getPrices(); // Ess information. + TimeOfUseTariffControllerImpl.Context context = globalContext.energyScheduleHandler().getContext(); final var essTotalEnergy = context.ess().getCapacity().getOrError(); final var essMinSocEnergy = getEssMinSocEnergy(context, essTotalEnergy); final var essMaxSocEnergy = round(ESS_MAX_SOC / 100F * essTotalEnergy); @@ -156,8 +153,8 @@ public static Params createSimulatorParams(Context context, final var essSocEnergy = essTotalEnergy /* [Wh] */ / 100 * essSoc; // Power Values for scheduling battery for individual periods. - var maxDischargePower = context.sum().getEssMaxDischargePower().orElse(1000 /* at least 1000 */); - var maxChargePower = context.sum().getEssMaxDischargePower().orElse(-1000 /* at least 1000 */); + var maxDischargePower = globalContext.sum().getEssMaxDischargePower().orElse(1000 /* at least 1000 */); + var maxChargePower = globalContext.sum().getEssMaxDischargePower().orElse(-1000 /* at least 1000 */); if (context.limitChargePowerFor14aEnWG()) { maxChargePower = max(ESS_LIMIT_14A_ENWG, maxChargePower); // Apply §14a EnWG limit } @@ -234,11 +231,11 @@ protected static boolean paramsAreValid(Params p) { * Returns the amount of energy that is not available for scheduling because of * a configured minimum SoC. * - * @param context the {@link Context} + * @param context the {@link TimeOfUseTariffControllerImpl.Context} * @param essCapacity net {@link SymmetricEss.ChannelId#CAPACITY} * @return the value in [Wh] */ - protected static int getEssMinSocEnergy(Context context, int essCapacity) { + protected static int getEssMinSocEnergy(TimeOfUseTariffControllerImpl.Context context, int essCapacity) { return essCapacity /* [Wh] */ / 100 // * getEssMinSocPercentage(// context.ctrlLimitTotalDischarges(), // @@ -371,53 +368,61 @@ protected static int findFirstValleyIndex(int fromIndex, double[] values) { * from the {@link Optimizer} provided, then concatenates them to generate a * 24-hour {@link GetScheduleResponse}. * - * @param optimizer the {@link Optimizer} - * @param requestId the JSON-RPC request-id - * @param timedata the{@link Timedata} - * @param componentId the Component-ID - * @param now the current {@link ZonedDateTime} (will get rounded down - * to 15 minutes) + * @param optimizer the {@link Optimizer} + * @param requestId the JSON-RPC request-id + * @param timedata the{@link Timedata} + * @param timeOfUseTariff the {@link TimeOfUseTariff} + * @param componentId the Component-ID + * @param now the current {@link ZonedDateTime} (will get rounded + * down to 15 minutes) * @return the {@link GetScheduleResponse} * @throws OpenemsNamedException on error */ public static GetScheduleResponse handleGetScheduleRequest(Optimizer optimizer, UUID requestId, Timedata timedata, - String componentId, ZonedDateTime now) throws OpenemsNamedException { + TimeOfUseTariff timeOfUseTariff, String componentId, ZonedDateTime now) { + final var b = ImmutableList.builder(); now = roundDownToQuarter(now); + final var fromTime = now.minusHours(3); - final var schedule = optimizer.getSchedule(); - if (schedule == null) { - throw new OpenemsException("Has no Schedule"); - } final var params = optimizer.getParams(); - if (params == null) { - throw new OpenemsException("Has no Params"); - } - final var channelQuarterlyPrices = new ChannelAddress(componentId, "QuarterlyPrices"); - final var channelStateMachine = new ChannelAddress(componentId, "StateMachine"); - final var b = ImmutableList.builder(); - - // Process past data - final var fromDate = now.minusHours(3); - final var toDate = now.minusMinutes(15); - - try { - var queryResult = timedata.queryHistoricData(null, fromDate, toDate, // - Set.of(channelQuarterlyPrices, channelStateMachine, // - SUM_GRID, SUM_PRODUCTION, SUM_CONSUMPTION, SUM_ESS_DISCHARGE_POWER, SUM_ESS_SOC), - new Resolution(15, ChronoUnit.MINUTES)); - ScheduleData.fromHistoricDataQuery(// - params.essTotalEnergy(), channelQuarterlyPrices, channelStateMachine, queryResult) // - .forEach(b::add); - } catch (Exception e) { - LOG.warn("Unable to read historic data: " + e.getMessage()); + if (params != null) { + // Process last three hours of historic data + final var channelQuarterlyPrices = new ChannelAddress(componentId, "QuarterlyPrices"); + final var channelStateMachine = new ChannelAddress(componentId, "StateMachine"); + try { + var queryResult = timedata.queryHistoricData(null, fromTime, now, // + Set.of(channelQuarterlyPrices, channelStateMachine, // + SUM_GRID, SUM_PRODUCTION, SUM_CONSUMPTION, SUM_ESS_DISCHARGE_POWER, SUM_ESS_SOC), + new Resolution(15, ChronoUnit.MINUTES)); + ScheduleData.fromHistoricDataQuery(// + params.essTotalEnergy(), channelQuarterlyPrices, channelStateMachine, queryResult) // + .forEach(b::add); + } catch (Exception e) { + LOG.warn("Unable to read historic data: " + e.getMessage()); + } } // Process future schedule + final var schedule = optimizer.getSchedule(); optimizer.getSchedule().values().stream() // .flatMap(ScheduleData::fromPeriod) // .forEach(b::add); - return new GetScheduleResponse(requestId, fromDate, new ScheduleDatas(params.essTotalEnergy(), b.build())); + // Find 'toTime' of result + final ZonedDateTime toTime; + if (!schedule.isEmpty()) { + toTime = schedule.lastKey(); + } else { + var pricesPerQuarter = timeOfUseTariff.getPrices().pricePerQuarter; + if (!pricesPerQuarter.isEmpty()) { + toTime = pricesPerQuarter.lastKey(); + } else { + toTime = fromTime; + } + } + + return new GetScheduleResponse(requestId, fromTime, toTime, + new ScheduleDatas(params.essTotalEnergy(), b.build())); } /** @@ -445,170 +450,34 @@ public static long calculateExecutionLimitSeconds(Clock clock) { * NOTE: heavy computation is ok here, because this method is called only at the * end with the best Schedule. * - * @param p the {@link Params} - * @param essInitialEnergy the initial ESS energy in this period * @param state the initial state - * @param ef the {@link EnergyFlow} + * @param efBalancing the {@link EnergyFlow} as it would be in + * {@link StateMachine#BALANCING} + * @param efDelayDischarge the {@link EnergyFlow} as it would be in + * {@link StateMachine#DELAY_DISCHARGE} + * @param efChargeGrid the {@link EnergyFlow} as it would be in + * {@link StateMachine#CHARGE_GRID} * @return the new state */ - public static StateMachine postprocessSimulatorState(Params p, int essInitialEnergy, StateMachine state, - EnergyFlow ef) { - return switch (state) { - case BALANCING -> state; - - case DELAY_DISCHARGE -> { - // DELAY_DISCHARGE,... - if (essInitialEnergy <= p.essMinSocEnergy()) { - // but battery is already empty (at Min-Soc) - yield BALANCING; - } else if (ef.productionToEss() > 0) { - // but actually charging -> could have been BALANCING - yield BALANCING; - } - yield state; - } - - case CHARGE_GRID -> { + public static StateMachine postprocessSimulatorState(StateMachine state, EnergyFlow efBalancing, + EnergyFlow efDelayDischarge, EnergyFlow efChargeGrid) { + if (state == CHARGE_GRID) { // CHARGE_GRID,... - if (ef.gridToEss() == 0) { - // but actually not charging - yield BALANCING; - } else if (essInitialEnergy > p.essMaxSocEnergy()) { - // but battery is above limit - yield DELAY_DISCHARGE; + if (efChargeGrid.ess() >= efDelayDischarge.ess()) { + // but battery charge/discharge is the same as DELAY_DISCHARGE + state = DELAY_DISCHARGE; } - yield state; } - }; - } - /** - * Post-Process a state during {@link Controller#run()}, i.e. replace with - * 'better' state if appropriate. - * - *

- * NOTE: this can be useful, if live operation deviates from predicted - * operation, e.g. because predictions were wrong. - * - * @param minSoc the configured Minimum-SoC, or zero - * @param soc the current {@link SymmetricEss.ChannelId#SOC} - * @param production the current {@link Sum.ChannelId#PRODUCTION_ACTIVE_POWER}, - * or zero - * @param state the initial state - * @return the new state - */ - public static StateMachine postprocessRunState(int minSoc, Integer soc, int production, StateMachine state) { - if (soc == null) { - return state; - } - - return switch (state) { - case BALANCING -> state; - - case DELAY_DISCHARGE -> { + if (state == DELAY_DISCHARGE) { // DELAY_DISCHARGE,... - if (soc <= minSoc) { - // but SoC is at Min-SoC -> could have been BALANCING - yield BALANCING; - } - yield state; - } - - case CHARGE_GRID -> { - // CHARGE_GRID,... - if (soc > ESS_MAX_SOC) { - // but surpassed Max-SoC -> stop charge; no discharge - yield DELAY_DISCHARGE; - } - yield state; - } - }; - } - - protected static int calculateEssChargeInChargeGridPowerFromParams(Params params, ManagedSymmetricEss ess) { - if (params != null) { - for (var period : params.optimizePeriods()) { - return toPower(period.essChargeInChargeGrid()); // take first period + if (efDelayDischarge.ess() >= efBalancing.ess()) { + // but battery charge/discharge is the same as BALANCING + state = BALANCING; } } - var capacity = ess.getCapacity(); - if (capacity.isDefined()) { - return round(capacity.get() * ESS_CHARGE_C_RATE); - } - var maxApparentPower = ess.getMaxApparentPower(); - if (maxApparentPower.isDefined()) { - return maxApparentPower.get() / 4; - } - return 0; - } - - /** - * Calculates the Max-ActivePower constraint for - * {@link StateMachine#CHARGE_GRID}. - * - * @param params the {@link Params} - * @param ess the {@link ManagedSymmetricEss} - * @param sum the {@link Sum} - * @param maxChargePowerFromGrid the configured max charge from grid power - * @param limitChargePowerFor14aEnWG Limit Charge Power for §14a EnWG - * @return the set-point or null - */ - public static Integer calculateChargeGridPower(Params params, ManagedSymmetricEss ess, Sum sum, - int maxChargePowerFromGrid, boolean limitChargePowerFor14aEnWG) { - // TODO limitChargePowerFor14aEnWG - var gridActivePower = sum.getGridActivePower().get(); // current buy-from/sell-to grid - var essActivePower = ess.getActivePower().get(); // current charge/discharge ESS - if (gridActivePower == null || essActivePower == null) { - return null; // undefined state - } - - var realGridPower = gridActivePower + essActivePower; // 'real', without current ESS charge/discharge - var targetChargePower = calculateEssChargeInChargeGridPowerFromParams(params, ess) // - + min(0, realGridPower) * -1; // add excess production - var effectiveGridBuyPower = max(0, realGridPower) + targetChargePower; - var chargePower = max(0, targetChargePower - max(0, effectiveGridBuyPower - maxChargePowerFromGrid)); - - // Invert to negative for CHARGE - chargePower *= -1; - // Apply §14a EnWG limit - if (limitChargePowerFor14aEnWG) { - chargePower = max(ESS_LIMIT_14A_ENWG, chargePower); - } - - return chargePower; - } - - /** - * Calculates the Max-ActivePower constraint for - * {@link StateMachine#CHARGE_PRODUCTION}. - * - * @param sum the {@link Sum} - * @return the set-point - */ - public static Integer calculateMaxChargeProductionPower(Sum sum) { - var productionAcActivePower = sum.getProductionAcActivePower().get(); - if (productionAcActivePower == null || productionAcActivePower < 0) { - return 0; // unknown AC production -> do not charge - } - return -productionAcActivePower; - } - - /** - * Calculates the ActivePower constraint for - * {@link StateMachine#DELAY_DISCHARGE}. - * - * @param ess the {@link ManagedSymmetricEss} - * @return the set-point - */ - public static Integer calculateDelayDischargePower(ManagedSymmetricEss ess) { - if (ess instanceof HybridEss) { - // Limit discharge to DC-PV power - return max(0, ess.getActivePower().orElse(0) - ((HybridEss) ess).getDcDischargePower().orElse(0)); - } else { - // Limit discharge to 0 - return 0; - } + return state; } /** diff --git a/io.openems.edge.energy/test/.gitignore b/io.openems.edge.energy/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.edge.energy/test/io/openems/edge/energy/EnergySchedulerImplTest.java b/io.openems.edge.energy/test/io/openems/edge/energy/EnergySchedulerImplTest.java new file mode 100644 index 00000000000..10331208f63 --- /dev/null +++ b/io.openems.edge.energy/test/io/openems/edge/energy/EnergySchedulerImplTest.java @@ -0,0 +1,126 @@ +package io.openems.edge.energy; + +import static io.openems.common.utils.DateUtils.roundDownToQuarter; +import static io.openems.edge.energy.TestData.CONSUMPTION_PREDICTION_QUARTERLY; +import static io.openems.edge.energy.TestData.HOURLY_PRICES_SUMMER; +import static io.openems.edge.energy.TestData.PRODUCTION_PREDICTION_QUARTERLY; +import static io.openems.edge.energy.optimizer.Utils.SUM_CONSUMPTION; +import static io.openems.edge.energy.optimizer.Utils.SUM_PRODUCTION; +import static java.time.temporal.ChronoUnit.DAYS; + +import java.time.Clock; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.List; +import java.util.function.Supplier; + +import org.junit.Test; + +import io.openems.common.test.TimeLeapClock; +import io.openems.edge.common.sum.DummySum; +import io.openems.edge.common.test.AbstractComponentTest.TestCase; +import io.openems.edge.common.test.ComponentTest; +import io.openems.edge.common.test.DummyComponentManager; +import io.openems.edge.common.test.DummyConfigurationAdmin; +import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImpl; +import io.openems.edge.energy.optimizer.GlobalContext; +import io.openems.edge.energy.optimizer.Optimizer; +import io.openems.edge.predictor.api.prediction.Prediction; +import io.openems.edge.predictor.api.test.DummyPredictor; +import io.openems.edge.predictor.api.test.DummyPredictorManager; +import io.openems.edge.timedata.test.DummyTimedata; +import io.openems.edge.timeofusetariff.test.DummyTimeOfUseTariffProvider; + +public class EnergySchedulerImplTest { + + public static final Clock CLOCK = new TimeLeapClock(Instant.parse("2020-03-04T14:19:00.00Z"), ZoneOffset.UTC); + + private static final String CTRL_ID = "ctrl0"; + + @Test + public void test() throws Exception { + create(CLOCK); + } + + /** + * Creates a {@link EnergySchedulerImplTest} instance. + * + * @param clock a {@link Clock} + * @return the object + * @throws Exception on error + */ + public static EnergySchedulerImpl create(Clock clock) throws Exception { + var now = roundDownToQuarter(ZonedDateTime.now(clock)); + final var midnight = now.truncatedTo(DAYS); + var componentManager = new DummyComponentManager(clock); + var sum = new DummySum(); + var predictor0 = new DummyPredictor("predictor0", componentManager, + Prediction.from(sum, SUM_PRODUCTION, midnight, PRODUCTION_PREDICTION_QUARTERLY), SUM_PRODUCTION); + var predictor1 = new DummyPredictor("predictor0", componentManager, + Prediction.from(sum, SUM_CONSUMPTION, midnight, CONSUMPTION_PREDICTION_QUARTERLY), SUM_CONSUMPTION); + var timeOfUseTariff = DummyTimeOfUseTariffProvider.fromHourlyPrices(clock, HOURLY_PRICES_SUMMER); + var ctrl = new TimeOfUseTariffControllerImpl(); // this is not fully activated; config is null + + var sut = new EnergySchedulerImpl(); + new ComponentTest(sut) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("componentManager", componentManager) // + .addReference("predictorManager", new DummyPredictorManager(predictor0, predictor1)) // + .addReference("timedata", new DummyTimedata("timedata0")) // + .addReference("timeOfUseTariff", timeOfUseTariff) // + .addReference("schedulables", List.of(ctrl)) // + .addReference("sum", sum) // + .activate(MyConfig.create() // + .setId(CTRL_ID) // + .setEnabled(false) // + .setEssId("ess0") // + .setEssMaxChargePower(5000) // + .setMaxChargePowerFromGrid(10000) // + .setLimitChargePowerFor14aEnWG(false) // + .build()) // + .next(new TestCase()); + return sut; + } + + /** + * Gets the {@link Optimizer} via Java Reflection. + * + * @param energyScheduler the {@link EnergySchedulerImpl} + * @return the object + * @throws Exception on error + */ + public static Optimizer getOptimizer(EnergySchedulerImpl energyScheduler) throws Exception { + var field = EnergySchedulerImpl.class.getDeclaredField("optimizer"); + field.setAccessible(true); + return (Optimizer) field.get(energyScheduler); + } + + /** + * Calls the 'createParams()' method in the {@link Optimizer} via Java + * Reflection. + * + * @param optimizer the {@link Optimizer} + * @throws Exception on error + */ + public static void callCreateParams(Optimizer optimizer) throws Exception { + var method = Optimizer.class.getDeclaredMethod("createParams"); + method.setAccessible(true); + method.invoke(optimizer); + } + + /** + * Gets the {@link GlobalContext} via Java Reflection. + * + * @param energyScheduler the {@link EnergySchedulerImpl} + * @return the object + * @throws Exception on error + */ + @SuppressWarnings("unchecked") + public static GlobalContext getGlobalContext(EnergySchedulerImpl energyScheduler) throws Exception { + var optimizer = getOptimizer(energyScheduler); + var field = Optimizer.class.getDeclaredField("globalContext"); + field.setAccessible(true); + return ((Supplier) field.get(optimizer)).get(); + } +} diff --git a/io.openems.edge.energy/test/io/openems/edge/energy/MyConfig.java b/io.openems.edge.energy/test/io/openems/edge/energy/MyConfig.java new file mode 100644 index 00000000000..2aeb9dc7bd3 --- /dev/null +++ b/io.openems.edge.energy/test/io/openems/edge/energy/MyConfig.java @@ -0,0 +1,74 @@ +package io.openems.edge.energy; + +import io.openems.common.test.AbstractComponentConfig; + +@SuppressWarnings("all") +public class MyConfig extends AbstractComponentConfig implements Config { + + protected static class Builder { + private String id; + private boolean enabled; + private String essId; + private int essMaxChargePower; + private int maxChargePowerFromGrid; + private boolean limitChargePowerFor14aEnWG; + + private Builder() { + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setEnabled(boolean enabled) { + this.enabled = enabled; + return this; + } + + public Builder setEssId(String essId) { + this.essId = essId; + return this; + } + + public Builder setEssMaxChargePower(int essMaxChargePower) { + this.essMaxChargePower = essMaxChargePower; + return this; + } + + public Builder setMaxChargePowerFromGrid(int maxChargePowerFromGrid) { + this.maxChargePowerFromGrid = maxChargePowerFromGrid; + return this; + } + + public Builder setLimitChargePowerFor14aEnWG(boolean limitChargePowerFor14aEnWG) { + this.limitChargePowerFor14aEnWG = limitChargePowerFor14aEnWG; + return this; + } + + public MyConfig build() { + return new MyConfig(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new Builder(); + } + + private final Builder builder; + + private MyConfig(Builder builder) { + super(Config.class, builder.id); + this.builder = builder; + } + + @Override + public boolean enabled() { + return this.builder.enabled; + } +} \ No newline at end of file diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/TestData.java b/io.openems.edge.energy/test/io/openems/edge/energy/TestData.java similarity index 98% rename from io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/TestData.java rename to io.openems.edge.energy/test/io/openems/edge/energy/TestData.java index 664f3b0d705..c89873819e7 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/TestData.java +++ b/io.openems.edge.energy/test/io/openems/edge/energy/TestData.java @@ -1,8 +1,10 @@ -package io.openems.edge.controller.ess.timeofusetariff; +package io.openems.edge.energy; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; +import io.openems.edge.controller.ess.timeofusetariff.StateMachine; + public class TestData { // Edge 888; 06.11.2023 diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/GetScheduleResponseTest.java b/io.openems.edge.energy/test/io/openems/edge/energy/jsonrpc/GetScheduleResponseTest.java similarity index 53% rename from io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/GetScheduleResponseTest.java rename to io.openems.edge.energy/test/io/openems/edge/energy/jsonrpc/GetScheduleResponseTest.java index a5d6d3df718..780b77f7b9d 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/jsonrpc/GetScheduleResponseTest.java +++ b/io.openems.edge.energy/test/io/openems/edge/energy/jsonrpc/GetScheduleResponseTest.java @@ -1,9 +1,9 @@ -package io.openems.edge.controller.ess.timeofusetariff.jsonrpc; +package io.openems.edge.energy.jsonrpc; import static io.openems.common.utils.JsonUtils.prettyToString; import static io.openems.common.utils.UuidUtils.getNilUuid; -import static io.openems.edge.controller.ess.timeofusetariff.jsonrpc.ScheduleDatasTest.SCHEDULE_DATAS; -import static io.openems.edge.controller.ess.timeofusetariff.jsonrpc.ScheduleDatasTest.TIME; +import static io.openems.edge.energy.optimizer.ScheduleDatasTest.SCHEDULE_DATAS; +import static io.openems.edge.energy.optimizer.SimulatorTest.TIME; import static org.junit.Assert.assertEquals; import org.junit.Test; @@ -14,7 +14,7 @@ public class GetScheduleResponseTest { @Test public void testToJsonObject() throws OpenemsNamedException { - var response = new GetScheduleResponse(getNilUuid(), TIME, SCHEDULE_DATAS); + var response = new GetScheduleResponse(getNilUuid(), TIME, TIME.plusMinutes(30), SCHEDULE_DATAS); assertEquals(""" { @@ -22,25 +22,35 @@ public void testToJsonObject() throws OpenemsNamedException { "id": "00000000-0000-0000-0000-000000000000", "result": { "schedule": [ + { + "timestamp": "2000-01-01T00:00:00Z", + "soc": null, + "production": null, + "consumption": null, + "state": null, + "price": null, + "ess": null, + "grid": null + }, { "timestamp": "2000-01-01T00:15:00Z", "soc": 6, - "production": 222, - "consumption": 333, + "production": 888, + "consumption": 1332, "state": 0, "price": 78.9, - "ess": 987, - "grid": 654 + "ess": 3948, + "grid": 2616 }, { "timestamp": "2000-01-01T00:30:00Z", "soc": 21, - "production": 444, - "consumption": 333, + "production": 1776, + "consumption": 1332, "state": 3, "price": 12.3, - "ess": 987, - "grid": 654 + "ess": 3948, + "grid": 2616 } ] } diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/EnergyFlowTest.java b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/EnergyFlowTest.java similarity index 87% rename from io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/EnergyFlowTest.java rename to io.openems.edge.energy/test/io/openems/edge/energy/optimizer/EnergyFlowTest.java index a9d8b9eec7b..d58ae40fc1d 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/EnergyFlowTest.java +++ b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/EnergyFlowTest.java @@ -1,5 +1,9 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; +import static io.openems.edge.energy.optimizer.Utils.postprocessSimulatorState; import static java.lang.Math.max; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -11,7 +15,7 @@ import org.junit.Test; import io.openems.common.function.TriFunction; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.OptimizePeriod; +import io.openems.edge.energy.optimizer.Params.OptimizePeriod; public class EnergyFlowTest { @@ -257,6 +261,7 @@ public void testChargeGridAndAboveGridMaxEnergy() { /* * DISCHARGE GRID - just for completeness */ + private static EnergyFlow withDischargeGrid(Params p, OptimizePeriod op, int essInitial) { // This is just for completeness; not actually used yet return EnergyFlow.create(p, op, essInitial, // @@ -275,4 +280,29 @@ public void testDischargeGridAndCharge() { assertEquals(500, e.productionToConsumption()); assertEquals(2000, e.productionToGrid()); } + + /* + * Utils + */ + + @Test + public void testUtilsPostprocessPeriodState() { + assertEquals("BALANCING stays BALANCING", // + BALANCING, postprocessSimulatorState(BALANCING, // + NO_FLOW, NO_FLOW, NO_FLOW)); + + assertEquals("DELAY_DISCHARGE stays DELAY_DISCHARGE", // + DELAY_DISCHARGE, postprocessSimulatorState(DELAY_DISCHARGE, // + NO_FLOW, charge(EnergyFlow::withDelayDischarge), NO_FLOW)); + assertEquals("DELAY_DISCHARGE to BALANCING", // + BALANCING, postprocessSimulatorState(DELAY_DISCHARGE, // + NO_FLOW, NO_FLOW, NO_FLOW)); + + assertEquals("CHARGE_GRID stays CHARGE_GRID", // + CHARGE_GRID, postprocessSimulatorState(CHARGE_GRID, // + NO_FLOW, NO_FLOW, charge(EnergyFlow::withChargeGrid))); + assertEquals("CHARGE_GRID to BALANCING", // + BALANCING, postprocessSimulatorState(CHARGE_GRID, // + NO_FLOW, NO_FLOW, NO_FLOW)); + } } diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/InitialPopulationUtilsTest.java b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/InitialPopulationUtilsTest.java similarity index 82% rename from io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/InitialPopulationUtilsTest.java rename to io.openems.edge.energy/test/io/openems/edge/energy/optimizer/InitialPopulationUtilsTest.java index 31c101e741e..ee7c8c1d448 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/InitialPopulationUtilsTest.java +++ b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/InitialPopulationUtilsTest.java @@ -1,16 +1,16 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.CONSUMPTION_888_20231106; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PRICES_888_20231106; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PRODUCTION_888_20231106; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.InitialPopulationUtils.buildInitialPopulation; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.SimulatorTest.hourlyToQuarterly; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.interpolateArray; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.toEnergy; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.UtilsTest.prepareExistingSchedule; +import static io.openems.edge.energy.TestData.CONSUMPTION_888_20231106; +import static io.openems.edge.energy.TestData.PRICES_888_20231106; +import static io.openems.edge.energy.TestData.PRODUCTION_888_20231106; +import static io.openems.edge.energy.optimizer.InitialPopulationUtils.buildInitialPopulation; +import static io.openems.edge.energy.optimizer.SimulatorTest.hourlyToQuarterly; +import static io.openems.edge.energy.optimizer.Utils.interpolateArray; +import static io.openems.edge.energy.optimizer.Utils.toEnergy; +import static io.openems.edge.energy.optimizer.UtilsTest.prepareExistingSchedule; import static java.util.Arrays.stream; import static org.junit.Assert.assertEquals; diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/IntegrationTests.java b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/IntegrationTests.java similarity index 79% rename from io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/IntegrationTests.java rename to io.openems.edge.energy/test/io/openems/edge/energy/optimizer/IntegrationTests.java index 8869a26d10a..f5b0ebbc544 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/IntegrationTests.java +++ b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/IntegrationTests.java @@ -1,11 +1,11 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; import static io.openems.common.utils.DateUtils.parseZonedDateTimeOrError; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Params.PARAMS_PATTERN; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.calculateCost; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.getBestSchedule; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.SimulatorTest.logSchedule; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.initializeRandomRegistryForUnitTest; +import static io.openems.edge.energy.optimizer.Params.PARAMS_PATTERN; +import static io.openems.edge.energy.optimizer.Simulator.calculateCost; +import static io.openems.edge.energy.optimizer.Simulator.getBestSchedule; +import static io.openems.edge.energy.optimizer.SimulatorTest.logSchedule; +import static io.openems.edge.energy.optimizer.Utils.initializeRandomRegistryForUnitTest; import static java.lang.Integer.parseInt; import static org.junit.Assert.assertEquals; @@ -20,8 +20,7 @@ import io.openems.common.exceptions.OpenemsException; import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.jsonrpc.ScheduleDatas; -import io.openems.edge.controller.ess.timeofusetariff.jsonrpc.ScheduleDatas.ScheduleData; +import io.openems.edge.energy.optimizer.ScheduleDatas.ScheduleData; public class IntegrationTests { diff --git a/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/OptimizerTest.java b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/OptimizerTest.java new file mode 100644 index 00000000000..1ab693e4e03 --- /dev/null +++ b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/OptimizerTest.java @@ -0,0 +1,21 @@ +package io.openems.edge.energy.optimizer; + +import static io.openems.edge.energy.EnergySchedulerImplTest.CLOCK; +import static io.openems.edge.energy.EnergySchedulerImplTest.getOptimizer; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import io.openems.edge.energy.EnergySchedulerImplTest; + +public class OptimizerTest { + + @Test + public void testEmpty() throws Exception { + var sut = getOptimizer(EnergySchedulerImplTest.create(CLOCK)); + assertNull(sut.getParams()); + assertTrue(sut.getSchedule().isEmpty()); + } + +} diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/ParamsUtilsTest.java b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/ParamsUtilsTest.java similarity index 83% rename from io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/ParamsUtilsTest.java rename to io.openems.edge.energy/test/io/openems/edge/energy/optimizer/ParamsUtilsTest.java index 61fa54d6c1f..68fb733131c 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/ParamsUtilsTest.java +++ b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/ParamsUtilsTest.java @@ -1,7 +1,7 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.ParamsUtils.calculateChargeEnergyInChargeGrid; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.ParamsUtils.calculatePeriodLengthHourFromIndex; +import static io.openems.edge.energy.optimizer.ParamsUtils.calculateChargeEnergyInChargeGrid; +import static io.openems.edge.energy.optimizer.ParamsUtils.calculatePeriodLengthHourFromIndex; import static org.junit.Assert.assertEquals; import java.time.ZonedDateTime; diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/RunOptimizerFromLogApp.java b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/RunOptimizerFromLogApp.java similarity index 72% rename from io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/RunOptimizerFromLogApp.java rename to io.openems.edge.energy/test/io/openems/edge/energy/optimizer/RunOptimizerFromLogApp.java index 935d969fc10..1c468f22028 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/RunOptimizerFromLogApp.java +++ b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/RunOptimizerFromLogApp.java @@ -1,7 +1,8 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.getBestSchedule; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.SimulatorTest.logSchedule; +import static io.openems.edge.energy.optimizer.Simulator.getBestSchedule; +import static io.openems.edge.energy.optimizer.Simulator.simulate; +import static io.openems.edge.energy.optimizer.Utils.logSchedule; /** * This little application allows running the Optimizer from an existing log. @@ -34,7 +35,8 @@ public class RunOptimizerFromLogApp { public static void main(String[] args) throws Exception { var params = IntegrationTests.parseParams(LOG); var schedule = getBestSchedule(params, EXECUTION_LIMIT_SECONDS); + var periods = simulate(params, schedule); - logSchedule(params, schedule); + logSchedule(params, periods); } } \ No newline at end of file diff --git a/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/ScheduleDatasTest.java b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/ScheduleDatasTest.java new file mode 100644 index 00000000000..33cc5327106 --- /dev/null +++ b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/ScheduleDatasTest.java @@ -0,0 +1,183 @@ +package io.openems.edge.energy.optimizer; + +import static io.openems.common.utils.JsonUtils.getAsInt; +import static io.openems.common.utils.JsonUtils.prettyToString; +import static io.openems.common.utils.JsonUtils.toJson; +import static io.openems.common.utils.JsonUtils.toJsonArray; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; +import static io.openems.edge.energy.EnergySchedulerImplTest.CLOCK; +import static io.openems.edge.energy.EnergySchedulerImplTest.getOptimizer; +import static io.openems.edge.energy.optimizer.ScheduleDatas.fromLogString; +import static io.openems.edge.energy.optimizer.ScheduleDatas.ScheduleData.fromHistoricDataQuery; +import static io.openems.edge.energy.optimizer.Utils.SUM_CONSUMPTION; +import static io.openems.edge.energy.optimizer.Utils.SUM_ESS_DISCHARGE_POWER; +import static io.openems.edge.energy.optimizer.Utils.SUM_ESS_SOC; +import static io.openems.edge.energy.optimizer.Utils.SUM_GRID; +import static io.openems.edge.energy.optimizer.Utils.SUM_PRODUCTION; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.SortedMap; + +import org.junit.Test; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSortedMap; +import com.google.gson.JsonElement; + +import io.openems.common.types.ChannelAddress; +import io.openems.common.utils.DateUtils; +import io.openems.edge.controller.ess.timeofusetariff.StateMachine; +import io.openems.edge.energy.EnergySchedulerImplTest; +import io.openems.edge.energy.optimizer.Params.Length; +import io.openems.edge.energy.optimizer.Params.OptimizePeriod; +import io.openems.edge.energy.optimizer.Params.QuarterPeriod; +import io.openems.edge.energy.optimizer.ScheduleDatas.ScheduleData; + +public class ScheduleDatasTest { + + protected static final ZonedDateTime TIME = ZonedDateTime.of(2000, 1, 1, 0, 15, 0, 0, ZoneId.of("UTC")); + + public static final ScheduleDatas SCHEDULE_DATAS = new ScheduleDatas(22_000, ImmutableList.of(// + new ScheduleData(TIME, null, 100, 200, 300, 1234, 222, 333, 78.9, DELAY_DISCHARGE, 987, 654), + new ScheduleData(TIME.plusMinutes(15), null, 100, 200, 300, 4567, 444, 333, 12.3, CHARGE_GRID, 987, 654))); + + @Test + public void testIsEmpty() { + assertFalse(SCHEDULE_DATAS.isEmpty()); + assertTrue(new ScheduleDatas(22_000, ImmutableList.of()).isEmpty()); + } + + @Test + public void testStream() { + assertEquals(2, SCHEDULE_DATAS.stream().count()); + } + + @Test + public void testToLogString() { + assertEquals( + """ + OPTIMIZER Time OptimizeBy EssMaxChargeEnergy EssMaxDischargeEnergy MaxBuyFromGrid EssInitial Production Consumption Price State EssChargeDischarge Grid + OPTIMIZER 00:15 - 100 200 300 1234 222 333 78.90 DELAY_DISCHARGE 987 654 + OPTIMIZER 00:30 - 100 200 300 4567 444 333 12.30 CHARGE_GRID 987 654 + """, + SCHEDULE_DATAS.toLogString("OPTIMIZER ")); + } + + @Test + public void testToJsonArray() { + assertEquals(""" + [ + { + "timestamp": "2000-01-01T00:15:00Z", + "soc": 6, + "production": 888, + "consumption": 1332, + "state": 0, + "price": 78.9, + "ess": 3948, + "grid": 2616 + }, + { + "timestamp": "2000-01-01T00:30:00Z", + "soc": 21, + "production": 1776, + "consumption": 1332, + "state": 3, + "price": 12.3, + "ess": 3948, + "grid": 2616 + } + ]""", prettyToString(SCHEDULE_DATAS.toJsonObjects().values().stream().collect(toJsonArray()))); + } + + @Test + public void testFromLogString() { + var log = SCHEDULE_DATAS.toLogString(""); + assertEquals(log, fromLogString(22_000, log).toLogString("")); + } + + @Test + public void testFromSchedule1() throws Exception { + var optimizer = getOptimizer(EnergySchedulerImplTest.create(CLOCK)); + var p = optimizer.getParams(); + var s = optimizer.getSchedule(); + + assertNull(p); + assertTrue(s.isEmpty()); + } + + @Test + public void testFromSchedule2() throws Exception { + var sds = ScheduleDatas.fromSchedule(22_000, ImmutableSortedMap.of(// + TIME, // + new Simulator.Period(// + new OptimizePeriod(TIME, Length.QUARTER, 1, 2, 3, 4, 5, 6, 7., ImmutableList.of(// + new QuarterPeriod(TIME, 1, 2, 3, 4, 5, 6, 7))), + StateMachine.BALANCING, 10_000, + new EnergyFlow(0, 0, 1000 /* ess */, 500 /* grid */, 0, 0, 0, 0, 0, 0)) // + )); + assertEquals( + """ + OPTIMIZER Time OptimizeBy EssMaxChargeEnergy EssMaxDischargeEnergy MaxBuyFromGrid EssInitial Production Consumption Price State EssChargeDischarge Grid + OPTIMIZER 00:15 QUARTER 1 2 4 10000 5 6 7.00 BALANCING 1000 500 + """, + sds.toLogString("OPTIMIZER ")); + } + + @Test + public void testFromHistoricDataQuery() throws Exception { + final var price = ChannelAddress.fromString("_sum/GridBuyPrice"); + final var state = ChannelAddress.fromString("ctrl0/StateMachine"); + final var time = DateUtils.roundDownToQuarter(ZonedDateTime.now()); + + var sd = fromHistoricDataQuery(10000 /* [Wh] */, price, state, + ImmutableSortedMap.>naturalOrder() // + .put(time, ImmutableSortedMap.naturalOrder() // + .put(SUM_ESS_SOC, toJson(50)) // + .put(SUM_PRODUCTION, toJson(123)) // + .put(SUM_CONSUMPTION, toJson(234)) // + .put(price, toJson(100)) // + .put(state, toJson(CHARGE_GRID.getValue())) // + .put(SUM_ESS_DISCHARGE_POWER, toJson(345)) // + .put(SUM_GRID, toJson(456)) // + .build()) // + .build()) // + .findFirst().get(); + + assertEquals(5000, sd.essInitial()); + assertEquals(123 / 4, sd.production()); + assertEquals(234 / 4, sd.consumption()); + assertEquals(100., sd.price(), 0.001); + assertEquals(CHARGE_GRID, sd.state()); + assertEquals(345 / 4, sd.essChargeDischarge()); + assertEquals(456 / 4, sd.grid()); + + var j = sd.toJsonObject(10000); + + assertEquals(50, getAsInt(j, "soc")); + assertEquals(120 /* rounding */, getAsInt(j, "production")); + assertEquals(232, getAsInt(j, "consumption")); + assertEquals(CHARGE_GRID.getValue(), getAsInt(j, "state")); + assertEquals(100., getAsInt(j, "price"), 0.001); + assertEquals(344 /* rounding */, getAsInt(j, "ess")); + assertEquals(456, getAsInt(j, "grid")); + } + + @Test + public void testToJsonObjects() throws Exception { + // Simulate duplicated timestamp + var sds = new ScheduleDatas(SCHEDULE_DATAS.essTotalEnergy(), ImmutableList.builder() // + .addAll(SCHEDULE_DATAS.entries()) // + .add(SCHEDULE_DATAS.entries().get(0)) // + .build()); + var j = sds.toJsonObjects(); + assertEquals(2, j.size()); + } + +} diff --git a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/SimulatorTest.java b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/SimulatorTest.java similarity index 90% rename from io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/SimulatorTest.java rename to io.openems.edge.energy/test/io/openems/edge/energy/optimizer/SimulatorTest.java index 6f34a0141b1..73cae4f79cb 100644 --- a/io.openems.edge.controller.ess.timeofusetariff/test/io/openems/edge/controller/ess/timeofusetariff/optimizer/SimulatorTest.java +++ b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/SimulatorTest.java @@ -1,15 +1,15 @@ -package io.openems.edge.controller.ess.timeofusetariff.optimizer; +package io.openems.edge.energy.optimizer; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.CHARGE_GRID; import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.CONSUMPTION_888_20231106; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PRICES_888_20231106; -import static io.openems.edge.controller.ess.timeofusetariff.TestData.PRODUCTION_888_20231106; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.getBestSchedule; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.simulate; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.interpolateArray; -import static io.openems.edge.controller.ess.timeofusetariff.optimizer.Utils.toEnergy; +import static io.openems.edge.energy.TestData.CONSUMPTION_888_20231106; +import static io.openems.edge.energy.TestData.PRICES_888_20231106; +import static io.openems.edge.energy.TestData.PRODUCTION_888_20231106; +import static io.openems.edge.energy.optimizer.Simulator.getBestSchedule; +import static io.openems.edge.energy.optimizer.Simulator.simulate; +import static io.openems.edge.energy.optimizer.Utils.interpolateArray; +import static io.openems.edge.energy.optimizer.Utils.toEnergy; import static java.util.Arrays.stream; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; @@ -30,7 +30,7 @@ import io.jenetics.util.RandomRegistry; import io.openems.edge.controller.ess.timeofusetariff.ControlMode; import io.openems.edge.controller.ess.timeofusetariff.StateMachine; -import io.openems.edge.controller.ess.timeofusetariff.optimizer.Simulator.Period; +import io.openems.edge.energy.optimizer.Simulator.Period; public class SimulatorTest { diff --git a/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/UtilsTest.java b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/UtilsTest.java new file mode 100644 index 00000000000..a2b82ad90a9 --- /dev/null +++ b/io.openems.edge.energy/test/io/openems/edge/energy/optimizer/UtilsTest.java @@ -0,0 +1,337 @@ +package io.openems.edge.energy.optimizer; + +import static io.openems.common.utils.DateUtils.roundDownToQuarter; +import static io.openems.edge.common.test.TestUtils.withValue; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.BALANCING; +import static io.openems.edge.controller.ess.timeofusetariff.StateMachine.DELAY_DISCHARGE; +import static io.openems.edge.energy.EnergySchedulerImplTest.getOptimizer; +import static io.openems.edge.energy.TestData.CONSUMPTION_PREDICTION_QUARTERLY; +import static io.openems.edge.energy.TestData.PAST_HOURLY_PRICES; +import static io.openems.edge.energy.TestData.PAST_SOC; +import static io.openems.edge.energy.TestData.PAST_STATES; +import static io.openems.edge.energy.TestData.PRODUCTION_888_20231106; +import static io.openems.edge.energy.TestData.PRODUCTION_PREDICTION_QUARTERLY; +import static io.openems.edge.energy.optimizer.EnergyFlowTest.NO_FLOW; +import static io.openems.edge.energy.optimizer.SimulatorTest.TIME; +import static io.openems.edge.energy.optimizer.Utils.SUM_CONSUMPTION; +import static io.openems.edge.energy.optimizer.Utils.SUM_ESS_DISCHARGE_POWER; +import static io.openems.edge.energy.optimizer.Utils.SUM_ESS_SOC; +import static io.openems.edge.energy.optimizer.Utils.SUM_GRID; +import static io.openems.edge.energy.optimizer.Utils.SUM_PRODUCTION; +import static io.openems.edge.energy.optimizer.Utils.calculateExecutionLimitSeconds; +import static io.openems.edge.energy.optimizer.Utils.findFirstPeakIndex; +import static io.openems.edge.energy.optimizer.Utils.findFirstValleyIndex; +import static io.openems.edge.energy.optimizer.Utils.generateProductionPrediction; +import static io.openems.edge.energy.optimizer.Utils.getEssMinSocEnergy; +import static io.openems.edge.energy.optimizer.Utils.interpolateArray; +import static io.openems.edge.energy.optimizer.Utils.joinConsumptionPredictions; +import static io.openems.edge.energy.optimizer.Utils.paramsAreValid; +import static io.openems.edge.energy.optimizer.Utils.toEnergy; +import static io.openems.edge.energy.optimizer.Utils.toPower; +import static io.openems.edge.energy.optimizer.Utils.updateSchedule; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.time.Duration; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.TreeMap; +import java.util.stream.IntStream; + +import org.junit.Test; + +import com.google.common.collect.ImmutableSortedMap; + +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.test.TimeLeapClock; +import io.openems.common.types.ChannelAddress; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.test.AbstractDummyOpenemsComponent; +import io.openems.edge.controller.ess.emergencycapacityreserve.ControllerEssEmergencyCapacityReserve; +import io.openems.edge.controller.ess.limittotaldischarge.ControllerEssLimitTotalDischarge; +import io.openems.edge.controller.ess.timeofusetariff.StateMachine; +import io.openems.edge.controller.ess.timeofusetariff.TimeOfUseTariffControllerImpl; +import io.openems.edge.energy.EnergySchedulerImplTest; +import io.openems.edge.energy.optimizer.Simulator.Period; +import io.openems.edge.timedata.test.DummyTimedata; + +public class UtilsTest { + + protected static ImmutableSortedMap prepareExistingSchedule(ZonedDateTime fromDate, + StateMachine... existingSchedule) { + return IntStream.range(0, existingSchedule.length) // + .mapToObj(Integer::valueOf) // + .collect(ImmutableSortedMap.toImmutableSortedMap( + ZonedDateTime::compareTo, // + i -> fromDate.plusMinutes(i * 15), // + i -> existingSchedule[i])); + } + + @Test + public void testInterpolateArrayFloat() { + assertArrayEquals(new double[] { 123, 123, 234, 234, 345 }, // + interpolateArray(new Double[] { null, 123., 234., null, 345., null }), // + 0.0001F); + + assertArrayEquals(new double[] {}, // + interpolateArray(new Double[] { null }), // + 0.0001F); + } + + @Test + public void testInterpolateArrayInteger() { + assertArrayEquals(new int[] { 123, 123, 234, 234, 345 }, // + interpolateArray(new Integer[] { null, 123, 234, null, 345, null })); + + assertArrayEquals(new int[] {}, // + interpolateArray(new Integer[] { null })); + + assertArrayEquals(new int[] { 123, 123 }, // + interpolateArray(new Integer[] { null, 123 })); + + assertArrayEquals(new int[] { 123 }, // + interpolateArray(new Integer[] { 123, null })); + } + + @Test + public void testToPower() { + assertEquals(2000, (int) toPower(500)); + assertNull(toPower(null)); + } + + @Test + public void testGenerateProductionPrediction() { + final var arr = new Integer[] { 1, 2, 3 }; + assertArrayEquals(arr, generateProductionPrediction(arr, 2)); + assertArrayEquals(new Integer[] { 1, 2, 3, 0 }, generateProductionPrediction(arr, 4)); + } + + @Test + public void testJoinConsumptionPredictions() { + assertArrayEquals(// + new Integer[] { 1, 2, 3, 4, 55, 66, 77, 88, 99 }, // + joinConsumptionPredictions(4, // + new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, // + new Integer[] { 11, 22, 33, 44, 55, 66, 77, 88, 99 })); + } + + @Test + public void testFindFirstPeakIndex() { + assertEquals(0, findFirstPeakIndex(0, new double[0])); + assertEquals(0, findFirstPeakIndex(0, new double[] { 1 })); + assertEquals(0, findFirstPeakIndex(0, new double[] { 1, 0 })); + assertEquals(1, findFirstPeakIndex(0, new double[] { 0, 1, 0 })); + assertEquals(1, findFirstPeakIndex(0, new double[] { 0, 1, 0, 1 })); + assertEquals(5, findFirstPeakIndex(5, new double[0])); + } + + @Test + public void testFindFirstValleyIndex() { + assertEquals(0, findFirstValleyIndex(0, new double[0])); + assertEquals(0, findFirstValleyIndex(0, new double[] { 1 })); + assertEquals(1, findFirstValleyIndex(0, new double[] { 1, 0 })); + assertEquals(0, findFirstValleyIndex(0, new double[] { 0, 1, 0 })); + assertEquals(2, findFirstValleyIndex(1, new double[] { 0, 1, 0, 1 })); + assertEquals(5, findFirstValleyIndex(5, new double[0])); + } + + @Test + public void testParamsAreValid() throws Exception { + var builder = Params.create() // + .setTime(TIME) // + .setEssInitialEnergy(0) // + .setEssTotalEnergy(22000) // + .setEssMinSocEnergy(2_000) // + .setEssMaxSocEnergy(20_000) // + .seMaxBuyFromGrid(toEnergy(24_000)) // + .seMaxBuyFromGrid(0) // + .setStates(new StateMachine[0]); + + // No periods are available + assertFalse(paramsAreValid(builder // + .setProductions() // + .setConsumptions() // + .setPrices() // + .build())); + + // Production and Consumption predictions are all zero + assertFalse(paramsAreValid(builder // + .setProductions(0, 0, 0) // + .setConsumptions(0, 0) // + .setPrices(123F) // + .build())); + + // Prices are all the same + assertFalse(paramsAreValid(builder // + .setProductions(0, 1, 3) // + .setConsumptions(0, 2) // + .setPrices(123F, 123F) // + .build())); + + // Finally got it right... + assertTrue(paramsAreValid(builder // + .setProductions(0, 1, 3) // + .setConsumptions(0, 2) // + .setPrices(123F, 124F) // + .build())); + assertEquals(2, builder.build().optimizePeriods().size()); + } + + private static class MyControllerEssLimitTotalDischarge + extends AbstractDummyOpenemsComponent + implements ControllerEssLimitTotalDischarge { + + protected MyControllerEssLimitTotalDischarge(Integer minSoc) { + super("ctrl0", // + OpenemsComponent.ChannelId.values(), // + ControllerEssLimitTotalDischarge.ChannelId.values() // + ); + withValue(this.getMinSocChannel(), minSoc); + } + + @Override + public void run() throws OpenemsNamedException { + } + + @Override + protected MyControllerEssLimitTotalDischarge self() { + return this; + } + } + + private static class MyControllerEssEmergencyCapacityReserve + extends AbstractDummyOpenemsComponent + implements ControllerEssEmergencyCapacityReserve { + + protected MyControllerEssEmergencyCapacityReserve(Integer reserveSoc) { + super("ctrl0", // + OpenemsComponent.ChannelId.values(), // + ControllerEssEmergencyCapacityReserve.ChannelId.values() // + ); + withValue(this.getActualReserveSocChannel(), reserveSoc); + } + + @Override + public void run() throws OpenemsNamedException { + } + + @Override + protected MyControllerEssEmergencyCapacityReserve self() { + return this; + } + } + + @Test + public void testGetEssMinSocEnergy() { + var t1 = new MyControllerEssLimitTotalDischarge(50); + var t2 = new MyControllerEssLimitTotalDischarge(null); + var t3 = new MyControllerEssEmergencyCapacityReserve(30); + assertEquals(5000, getEssMinSocEnergy( + new TimeOfUseTariffControllerImpl.Context(List.of(t3), List.of(t1, t2), null, null, 10000, false), + 10000)); + } + + @Test + public void testHandleScheduleRequest() throws Exception { + final var clock = new TimeLeapClock(Instant.parse("2020-03-04T14:19:00.00Z"), ZoneOffset.UTC); + final var energyScheduler = EnergySchedulerImplTest.create(clock); + + // Simulate historic data + var now = roundDownToQuarter(ZonedDateTime.now(clock)); + final var fromDate = now.minusHours(3); + var timedata = new DummyTimedata("timedata0"); + for (var i = 0; i < 12; i++) { + var quarter = fromDate.plusMinutes(i * 15); + timedata.add(quarter, new ChannelAddress("ctrl0", "QuarterlyPrices"), PAST_HOURLY_PRICES[i]); + timedata.add(quarter, new ChannelAddress("ctrl0", "StateMachine"), PAST_STATES[i]); + timedata.add(quarter, SUM_PRODUCTION, PRODUCTION_PREDICTION_QUARTERLY[i]); + timedata.add(quarter, SUM_CONSUMPTION, CONSUMPTION_PREDICTION_QUARTERLY[i]); + timedata.add(quarter, SUM_ESS_SOC, PAST_SOC[i]); + timedata.add(quarter, SUM_ESS_DISCHARGE_POWER, PRODUCTION_888_20231106[i]); + timedata.add(quarter, SUM_GRID, PRODUCTION_888_20231106[i]); + } + + var optimizer = getOptimizer(energyScheduler); + System.out.println(optimizer); + // TODO this requires a fully configured TimeOfUseTariffControllerImpl + // callCreateParams(optimizer); + // + // // Testing only past data. For full data, optimizer has to be created as + // well. + // var result = handleGetScheduleRequest(optimizer, getNilUuid(), timedata, + // "ctrl0", clock.now()).getResult(); + // + // // JsonUtils.prettyPrint(result); + // + // var schedule = getAsJsonArray(result, "schedule"); + // assertEquals(11, schedule.size()); + // { + // var period = getAsJsonObject(schedule.get(0)); + // assertEquals(PAST_HOURLY_PRICES[0], getAsFloat(period, "price"), 0.00F); + // assertEquals(PRODUCTION_PREDICTION_QUARTERLY[0] / 4, getAsInt(period, + // "production")); + // } + } + + @Test + public void testCalculateExecutionLimitSeconds() { + final var clock = new TimeLeapClock(Instant.parse("2022-01-01T00:00:00.00Z"), ZoneOffset.UTC); + assertEquals(Duration.ofMinutes(14).plusSeconds(30).toSeconds(), calculateExecutionLimitSeconds(clock)); + + clock.leap(11, ChronoUnit.MINUTES); + assertEquals(Duration.ofMinutes(3).plusSeconds(30).toSeconds(), calculateExecutionLimitSeconds(clock)); + + clock.leap(150, ChronoUnit.SECONDS); + assertEquals(60, calculateExecutionLimitSeconds(clock)); + + clock.leap(1, ChronoUnit.SECONDS); + assertEquals(Duration.ofMinutes(15).plusSeconds(59).toSeconds(), calculateExecutionLimitSeconds(clock)); + } + + @Test + public void testUpdateSchedule() { + final ZonedDateTime t = ZonedDateTime.of(2000, 1, 1, 0, 0, 0, 0, ZoneId.of("UTC")); + final Period pOld = new Period(null, DELAY_DISCHARGE, 0, NO_FLOW); + final Period pNew = new Period(null, BALANCING, 0, NO_FLOW); + + var schedule = new TreeMap(); + schedule.put(t.minusMinutes(15), pOld); // old entry is removed + schedule.put(t, pOld); // current entry stays + schedule.put(t.plusMinutes(15), pOld); // is overridden + schedule.put(t.plusMinutes(30), pOld); // is overridden + schedule.put(t.plusMinutes(45), pOld); // timestamp is missing in new Schedule -> remove + + var newSchedule = ImmutableSortedMap.naturalOrder() // + .put(t, pNew) // + .put(t.plusMinutes(15), pNew) // + .put(t.plusMinutes(30), pNew) // + .build(); + + updateSchedule(t, schedule, newSchedule); + + // One old entry + assertEquals(1, schedule.values().stream().filter(v -> v == pOld).count()); + + // Two new entries + assertEquals(2, schedule.values().stream().filter(v -> v == pNew).count()); + + // No old entry + assertEquals(0, schedule.keySet().stream().filter(tz -> tz.isBefore(t)).count()); + + // Details + assertEquals(pOld, schedule.get(t)); + assertEquals(pNew, schedule.get(t.plusMinutes(15))); + assertEquals(pNew, schedule.get(t.plusMinutes(30))); + + // No current entry -> handle null + schedule.remove(t); + updateSchedule(t, schedule, newSchedule); + } +} diff --git a/io.openems.edge.ess.adstec.storaxe/src/io/openems/edge/ess/adstec/storaxe/EssAdstecStoraxeImpl.java b/io.openems.edge.ess.adstec.storaxe/src/io/openems/edge/ess/adstec/storaxe/EssAdstecStoraxeImpl.java index 9ad0862dfd1..6a045d2e415 100644 --- a/io.openems.edge.ess.adstec.storaxe/src/io/openems/edge/ess/adstec/storaxe/EssAdstecStoraxeImpl.java +++ b/io.openems.edge.ess.adstec.storaxe/src/io/openems/edge/ess/adstec/storaxe/EssAdstecStoraxeImpl.java @@ -80,7 +80,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var offset = -1; // The Modbus library seems to use 0 offsets. // We need to override because the ess returns neg for capacitative, pos for diff --git a/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/EssFeneconBydContainerImpl.java b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/EssFeneconBydContainerImpl.java index 58dee2ba3aa..621bbca994a 100644 --- a/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/EssFeneconBydContainerImpl.java +++ b/io.openems.edge.ess.byd.container/src/io/openems/edge/ess/byd/container/EssFeneconBydContainerImpl.java @@ -220,7 +220,7 @@ public Constraint[] getStaticConstraints() throws OpenemsNamedException { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, new FC3ReadRegistersTask(0x1001, Priority.LOW, // TODO check each channels id's for scaling factor m(EssFeneconBydContainer.ChannelId.PCS_SYSTEM_WORKSTATE, new UnsignedWordElement(0x1001)), @@ -496,7 +496,7 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { ))); } - private ModbusProtocol defineModbus1Protocol() throws OpenemsException { + private ModbusProtocol defineModbus1Protocol() { return new ModbusProtocol(this, new FC3ReadRegistersTask(0x3410, Priority.LOW, m(EssFeneconBydContainer.ChannelId.CONTAINER_IMMERSION_STATE, new UnsignedWordElement(0x3410)), @@ -570,7 +570,7 @@ private ModbusProtocol defineModbus1Protocol() throws OpenemsException { ))); } - protected ModbusProtocol defineModbus2Protocol() throws OpenemsException { + protected ModbusProtocol defineModbus2Protocol() { return new ModbusProtocol(this, new FC3ReadRegistersTask(0x38A0, Priority.LOW, // // RTU registers m(EssFeneconBydContainer.ChannelId.SYSTEM_WORKSTATE, new UnsignedWordElement(0x38A0)), diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40Impl.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40Impl.java index 6db503707f7..e4582a1474a 100644 --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40Impl.java +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/EssFeneconCommercial40Impl.java @@ -166,7 +166,7 @@ public String getModbusBridgeId() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(0x0101, Priority.LOW, // m(EssFeneconCommercial40.ChannelId.SYSTEM_STATE, new UnsignedWordElement(0x0101)), diff --git a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/AbstractEssFeneconCommercial40Pv.java b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/AbstractEssFeneconCommercial40Pv.java index 01efbb553d7..b55b0b549ca 100644 --- a/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/AbstractEssFeneconCommercial40Pv.java +++ b/io.openems.edge.ess.fenecon.commercial40/src/io/openems/edge/ess/fenecon/commercial40/charger/AbstractEssFeneconCommercial40Pv.java @@ -6,7 +6,6 @@ import org.osgi.service.event.EventHandler; import io.openems.common.channel.AccessMode; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.ModbusComponent; import io.openems.edge.bridge.modbus.api.ModbusProtocol; @@ -52,7 +51,7 @@ public AbstractEssFeneconCommercial40Pv() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var protocol = new ModbusProtocol(this, // new FC16WriteRegistersTask(0x0503, // m(EssFeneconCommercial40Pv.ChannelId.SET_PV_POWER_LIMIT, new UnsignedWordElement(0x0503), diff --git a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GridconPcsImpl.java b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GridconPcsImpl.java index e70b13d4ba9..22a1f17994b 100644 --- a/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GridconPcsImpl.java +++ b/io.openems.edge.ess.mr.gridcon/src/io/openems/edge/ess/mr/gridcon/GridconPcsImpl.java @@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.BridgeModbus; import io.openems.edge.bridge.modbus.api.ModbusComponent; @@ -382,7 +381,7 @@ protected void writeDcDcControlCommandWord() throws IllegalArgumentException, Op } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { int inverterCount = this.inverterCount.getCount(); ModbusProtocol result = new ModbusProtocol(this, // diff --git a/io.openems.edge.ess.sma/src/io/openems/edge/sma/sunnyisland/EssSmaSunnyIslandImpl.java b/io.openems.edge.ess.sma/src/io/openems/edge/sma/sunnyisland/EssSmaSunnyIslandImpl.java index feca1e58f8a..49513f81dc0 100644 --- a/io.openems.edge.ess.sma/src/io/openems/edge/sma/sunnyisland/EssSmaSunnyIslandImpl.java +++ b/io.openems.edge.ess.sma/src/io/openems/edge/sma/sunnyisland/EssSmaSunnyIslandImpl.java @@ -153,7 +153,7 @@ public void applyPower(int activePowerL1, int reactivePowerL1, int activePowerL2 } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(30051, Priority.LOW, // m(EssSmaSunnyIsland.ChannelId.DEVICE_CLASS, new UnsignedDoublewordElement(30051)), // diff --git a/io.openems.edge.evcs.alpitronic.hypercharger/src/io/openems/edge/evcs/hypercharger/EvcsAlpitronicHyperchargerImpl.java b/io.openems.edge.evcs.alpitronic.hypercharger/src/io/openems/edge/evcs/hypercharger/EvcsAlpitronicHyperchargerImpl.java index c3442232ad9..0dec8b68dad 100644 --- a/io.openems.edge.evcs.alpitronic.hypercharger/src/io/openems/edge/evcs/hypercharger/EvcsAlpitronicHyperchargerImpl.java +++ b/io.openems.edge.evcs.alpitronic.hypercharger/src/io/openems/edge/evcs/hypercharger/EvcsAlpitronicHyperchargerImpl.java @@ -170,7 +170,7 @@ public void handleEvent(Event event) { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var modbusProtocol = new ModbusProtocol(this, new FC3ReadRegistersTask(this.offset.apply(0), Priority.LOW, diff --git a/io.openems.edge.evcs.spelsberg/src/io/openems/edge/evcs/spelsberg/smart/EvcsSpelsbergSmartImpl.java b/io.openems.edge.evcs.spelsberg/src/io/openems/edge/evcs/spelsberg/smart/EvcsSpelsbergSmartImpl.java index aad3dc375e8..71e6df3342a 100644 --- a/io.openems.edge.evcs.spelsberg/src/io/openems/edge/evcs/spelsberg/smart/EvcsSpelsbergSmartImpl.java +++ b/io.openems.edge.evcs.spelsberg/src/io/openems/edge/evcs/spelsberg/smart/EvcsSpelsbergSmartImpl.java @@ -116,7 +116,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var modbusProtocol = new ModbusProtocol(this, new FC3ReadRegistersTask(1000, Priority.HIGH, m(EvcsSpelsbergSmart.ChannelId.CHARGE_POINT_STATE, new UnsignedWordElement(1000)), diff --git a/io.openems.edge.evcs.webasto.next/src/io/openems/edge/evcs/webasto/next/EvcsWebastoNextImpl.java b/io.openems.edge.evcs.webasto.next/src/io/openems/edge/evcs/webasto/next/EvcsWebastoNextImpl.java index fb56b811741..d57c72553c9 100644 --- a/io.openems.edge.evcs.webasto.next/src/io/openems/edge/evcs/webasto/next/EvcsWebastoNextImpl.java +++ b/io.openems.edge.evcs.webasto.next/src/io/openems/edge/evcs/webasto/next/EvcsWebastoNextImpl.java @@ -139,7 +139,7 @@ private void applyConfig(ComponentContext context, Config config) { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { // Cannot read the gaps, therefore there are so many tasks var modbusProtocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(1000, Priority.HIGH, diff --git a/io.openems.edge.evcs.webasto.unite/src/io/openems/edge/evcs/webasto/unite/EvcsWebastoUniteImpl.java b/io.openems.edge.evcs.webasto.unite/src/io/openems/edge/evcs/webasto/unite/EvcsWebastoUniteImpl.java index 3a144fd5797..8c0d5a821f6 100644 --- a/io.openems.edge.evcs.webasto.unite/src/io/openems/edge/evcs/webasto/unite/EvcsWebastoUniteImpl.java +++ b/io.openems.edge.evcs.webasto.unite/src/io/openems/edge/evcs/webasto/unite/EvcsWebastoUniteImpl.java @@ -114,7 +114,7 @@ protected void setModbus(BridgeModbus modbus) { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { /* * The Webasto Unite does not support reading Multiple Registers in one task * with "gaps" in between. Therefore, this modbus protocol consists of many diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/AbstractFeneconDessCharger.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/AbstractFeneconDessCharger.java index cf21f3796e3..638976fc8f3 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/AbstractFeneconDessCharger.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/charger/AbstractFeneconDessCharger.java @@ -6,7 +6,6 @@ import org.osgi.service.event.EventHandler; import io.openems.common.channel.AccessMode; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.ModbusComponent; import io.openems.edge.bridge.modbus.api.ModbusProtocol; @@ -41,7 +40,7 @@ public AbstractFeneconDessCharger() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var offset = this.getOffset(); return new ModbusProtocol(this, // new FC3ReadRegistersTask(offset + 2, Priority.LOW, // diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/FeneconDessEssImpl.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/FeneconDessEssImpl.java index e6ff71b8885..cfe3251c770 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/FeneconDessEssImpl.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/ess/FeneconDessEssImpl.java @@ -115,7 +115,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(10000, Priority.LOW, // m(FeneconDessEss.ChannelId.SYSTEM_STATE, new UnsignedWordElement(10000)), // diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/FeneconDessGridMeterImpl.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/FeneconDessGridMeterImpl.java index 5e0a1ff2cc5..39580294e38 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/FeneconDessGridMeterImpl.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/gridmeter/FeneconDessGridMeterImpl.java @@ -98,7 +98,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(11109, Priority.LOW, // m(FeneconDessGridMeter.ChannelId.ORIGINAL_ACTIVE_CONSUMPTION_ENERGY, diff --git a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/FeneconDessPvMeterImpl.java b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/FeneconDessPvMeterImpl.java index 676206cac33..e9fc1e27ddf 100644 --- a/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/FeneconDessPvMeterImpl.java +++ b/io.openems.edge.fenecon.dess/src/io/openems/edge/fenecon/dess/pvmeter/FeneconDessPvMeterImpl.java @@ -89,7 +89,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(11144, Priority.HIGH, // m(ElectricityMeter.ChannelId.ACTIVE_POWER_L1, new UnsignedWordElement(11144), DELTA_10000)), // diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/FeneconMiniEssImpl.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/FeneconMiniEssImpl.java index 29075f57aa6..397b253f49d 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/FeneconMiniEssImpl.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/ess/FeneconMiniEssImpl.java @@ -170,7 +170,7 @@ public SinglePhase getPhase() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(100, Priority.LOW, // m(FeneconMiniEss.ChannelId.SYSTEM_STATE, new UnsignedWordElement(100)), // diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/FeneconMiniGridMeterImpl.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/FeneconMiniGridMeterImpl.java index 33852c82a7e..9968e9c6e59 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/FeneconMiniGridMeterImpl.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/gridmeter/FeneconMiniGridMeterImpl.java @@ -92,7 +92,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(4004, Priority.HIGH, // m(ElectricityMeter.ChannelId.ACTIVE_POWER, new SignedWordElement(4004), diff --git a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/FeneconMiniPvMeterImpl.java b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/FeneconMiniPvMeterImpl.java index d382325cb7e..009311b915e 100644 --- a/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/FeneconMiniPvMeterImpl.java +++ b/io.openems.edge.fenecon.mini/src/io/openems/edge/fenecon/mini/pvmeter/FeneconMiniPvMeterImpl.java @@ -88,7 +88,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(4006, Priority.HIGH, // m(ElectricityMeter.ChannelId.ACTIVE_POWER, new UnsignedWordElement(4006)))); diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/FeneconProEssImpl.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/FeneconProEssImpl.java index 66bd61011ea..7b49c117c15 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/FeneconProEssImpl.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/ess/FeneconProEssImpl.java @@ -135,7 +135,7 @@ public String getModbusBridgeId() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(100, Priority.HIGH, // m(FeneconProEss.ChannelId.SYSTEM_STATE, new UnsignedWordElement(100)), // diff --git a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/FeneconProPvMeterImpl.java b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/FeneconProPvMeterImpl.java index 61837c0e395..896686de264 100644 --- a/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/FeneconProPvMeterImpl.java +++ b/io.openems.edge.fenecon.pro/src/io/openems/edge/fenecon/pro/pvmeter/FeneconProPvMeterImpl.java @@ -92,7 +92,7 @@ public String getModbusBridgeId() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(121, Priority.LOW, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new UnsignedWordElement(121), SCALE_FACTOR_2), // diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImpl.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImpl.java index c20446244e5..8dec660e38a 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImpl.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImpl.java @@ -312,8 +312,8 @@ private void setBatteryLimits(Battery battery) throws OpenemsNamedException { var setBatteryStrings = TypeUtils.divide(battery.getDischargeMinVoltage().get(), MODULE_MIN_VOLTAGE); var setChargeMaxCurrent = this.getGoodweType().maxDcCurrent; var setDischargeMaxCurrent = this.getGoodweType().maxDcCurrent; - var setChargeMaxVoltage = battery.getChargeMaxVoltage().orElse(0); - var setDischargeMinVoltage = battery.getDischargeMinVoltage().orElse(0); + var setChargeMaxVoltage = battery.getChargeMaxVoltage().orElse(210); + var setDischargeMinVoltage = battery.getDischargeMinVoltage().orElse(210); Integer setSocUnderMin = 0; // [0-100]; 0 MinSoc = 100 DoD Integer setOfflineSocUnderMin = 0; // [0-100]; 0 MinSoc = 100 DoD @@ -336,6 +336,9 @@ private void setBatteryLimits(Battery battery) throws OpenemsNamedException { } } + /* + * Check correct BMS register values. Goodwe recommends setting the values once + */ if (bmsChargeMaxCurrent.isDefined() && !Objects.equals(bmsChargeMaxCurrent.get(), setChargeMaxCurrent) || bmsDischargeMaxCurrent.isDefined() && !Objects.equals(bmsDischargeMaxCurrent.get(), setDischargeMaxCurrent) @@ -365,6 +368,24 @@ private void setBatteryLimits(Battery battery) throws OpenemsNamedException { this.writeToChannel(GoodWe.ChannelId.BMS_OFFLINE_SOC_UNDER_MIN, setOfflineSocUnderMin); } + /* + * Check correct BMS register value for voltage. Handled separately to avoid + * sending other values multiple times if the voltage registers are not written + * immediately + */ + if (doSetBmsVoltage(battery, bmsChargeMaxVoltage, setChargeMaxVoltage, bmsDischargeMinVoltage, + setDischargeMinVoltage)) { + // Update is required + this.logInfo(this.log, "Update for BMS Registers." // + + " Voltages" // + + " [Discharge " + bmsDischargeMinVoltage.get() + " -> " + setDischargeMinVoltage + "]" // + + " [Charge " + bmsChargeMaxVoltage.get() + " -> " + setChargeMaxVoltage + + "]. This can take up to 10 minutes."); + + this.writeToChannel(GoodWe.ChannelId.BMS_CHARGE_MAX_VOLTAGE, setChargeMaxVoltage); + this.writeToChannel(GoodWe.ChannelId.BMS_DISCHARGE_MIN_VOLTAGE, setDischargeMinVoltage); + } + /* * Regularly write all WBMS Channels. */ @@ -398,6 +419,25 @@ private void setBatteryLimits(Battery battery) throws OpenemsNamedException { this.writeToChannel(GoodWe.ChannelId.WBMS_DISABLE_TIMEOUT_DETECTION, 0); } + protected static boolean doSetBmsVoltage(Battery battery, Value bmsChargeMaxVoltage, + Integer setChargeMaxVoltage, Value bmsDischargeMinVoltage, Integer setDischargeMinVoltage) { + if (!battery.getChargeMaxCurrent().isDefined() || !battery.getDischargeMaxCurrent().isDefined() + || !bmsChargeMaxVoltage.isDefined() || !bmsChargeMaxVoltage.isDefined()) { + // Do not set channels if input data is missing/not yet available + return false; + } + if (battery.getChargeMaxCurrent().get() == 0 || battery.getDischargeMaxCurrent().get() == 0) { + // Exclude times with full or empty battery, due to inverter mis-behaviour + return false; + } + if (Objects.equals(bmsChargeMaxVoltage.get(), setChargeMaxVoltage) + && Objects.equals(bmsDischargeMinVoltage.get(), setDischargeMinVoltage)) { + // Values are already set + return false; + } + return true; + } + /** * Set general values. * diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/AbstractGoodWeEtCharger.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/AbstractGoodWeEtCharger.java index 9d04b476aad..cf6d669406e 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/AbstractGoodWeEtCharger.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/AbstractGoodWeEtCharger.java @@ -6,7 +6,6 @@ import org.osgi.service.event.EventHandler; import io.openems.common.channel.AccessMode; -import io.openems.common.exceptions.OpenemsException; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.ModbusComponent; import io.openems.edge.bridge.modbus.api.ModbusProtocol; @@ -47,7 +46,7 @@ protected AbstractGoodWeEtCharger(io.openems.edge.common.channel.ChannelId[] fir } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var startAddress = this.getStartAddress(); return new ModbusProtocol(this, // new FC3ReadRegistersTask(startAddress, Priority.HIGH, // diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/mppt/twostring/Config.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/mppt/twostring/Config.java new file mode 100644 index 00000000000..08ab8878666 --- /dev/null +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/mppt/twostring/Config.java @@ -0,0 +1,30 @@ +package io.openems.edge.goodwe.charger.mppt.twostring; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +@ObjectClassDefinition(// + name = "GoodWe Charger MPPT Two-String", // + description = "Implements the GoodWe-ET MPPT with two strings as one Charger.") + +@interface Config { + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") + String id() default "charger0"; + + @AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID") + String alias() default ""; + + @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") + boolean enabled() default true; + + @AttributeDefinition(name = "Used MPPT", description = "MPPT port that is used with one or two strings") + MpptPort mpptPort() default MpptPort.MPPT_1; + + @AttributeDefinition(name = "GoodWe ESS or Battery-Inverter", description = "ID of GoodWe Energy Storage System or Battery-Inverter.") + String essOrBatteryInverter_id() default "batteryInverter0"; + + @AttributeDefinition(name = "GoodWe ESS or Battery-Inverter target filter", description = "This is auto-generated by 'GoodWe ESS or Battery-Inverter'.") + String essOrBatteryInverter_target() default "(enabled=true)"; + + String webconsole_configurationFactory_nameHint() default "GoodWe Charger MPPT Two-String [{id}]"; +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/mppt/twostring/GoodWeChargerMpptTwoStringImpl.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/mppt/twostring/GoodWeChargerMpptTwoStringImpl.java new file mode 100644 index 00000000000..b6efa0f10c0 --- /dev/null +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/mppt/twostring/GoodWeChargerMpptTwoStringImpl.java @@ -0,0 +1,247 @@ +package io.openems.edge.goodwe.charger.mppt.twostring; + +import java.util.Optional; +import java.util.function.Consumer; + +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventHandler; +import org.osgi.service.event.propertytypes.EventTopics; +import org.osgi.service.metatype.annotations.Designate; + +import io.openems.common.channel.AccessMode; +import io.openems.common.exceptions.OpenemsException; +import io.openems.common.types.ChannelAddress; +import io.openems.common.types.OpenemsType; +import io.openems.edge.bridge.modbus.api.ModbusComponent; +import io.openems.edge.common.channel.IntegerReadChannel; +import io.openems.edge.common.channel.value.Value; +import io.openems.edge.common.component.AbstractOpenemsComponent; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.event.EdgeEventConstants; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.common.modbusslave.ModbusSlaveTable; +import io.openems.edge.common.type.TypeUtils; +import io.openems.edge.ess.dccharger.api.EssDcCharger; +import io.openems.edge.goodwe.charger.GoodWeCharger; +import io.openems.edge.goodwe.common.GoodWe; +import io.openems.edge.timedata.api.Timedata; +import io.openems.edge.timedata.api.TimedataProvider; +import io.openems.edge.timedata.api.utils.CalculateEnergyFromPower; + +@Designate(ocd = Config.class, factory = true) +@Component(// + name = "GoodWe.Charger.Mppt.Two-String", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) +@EventTopics({ // + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // +}) +public class GoodWeChargerMpptTwoStringImpl extends AbstractOpenemsComponent + implements EssDcCharger, GoodWeCharger, OpenemsComponent, EventHandler, TimedataProvider, ModbusSlave { + + private final CalculateEnergyFromPower calculateActualEnergy = new CalculateEnergyFromPower(this, + EssDcCharger.ChannelId.ACTUAL_ENERGY); + + private GoodWeListener currentListener; + private GoodWeListener powerListener; + private GoodWeListener voltageListener; + private Config config; + private boolean timedataQueryIsRunning = false; + + @Reference + private ConfigurationAdmin cm; + + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + private GoodWe essOrBatteryInverter; + + @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) + private volatile Timedata timedata = null; + + public GoodWeChargerMpptTwoStringImpl() { + super(// + OpenemsComponent.ChannelId.values(), // + ModbusComponent.ChannelId.values(), // + EssDcCharger.ChannelId.values(), // + GoodWeCharger.ChannelId.values() // + ); + } + + @Activate + private void activate(ComponentContext context, Config config) throws OpenemsException { + super.activate(context, config.id(), config.alias(), config.enabled()); + this.config = config; + + this.currentListener = new GoodWeListener(this, this.essOrBatteryInverter, + config.mpptPort().mpptCurrentChannelId, EssDcCharger.ChannelId.CURRENT); + this.powerListener = new GoodWeListener(this, this.essOrBatteryInverter, config.mpptPort().mpptPowerChannelId, + EssDcCharger.ChannelId.ACTUAL_POWER); + this.voltageListener = new GoodWeListener(this, this.essOrBatteryInverter, + config.mpptPort().mpptVoltageChannelId, EssDcCharger.ChannelId.VOLTAGE); + + this.essOrBatteryInverter.addCharger(this); + + if (OpenemsComponent.updateReferenceFilter(this.cm, this.servicePid(), "essOrBatteryInverter", + config.essOrBatteryInverter_id())) { + return; + } + } + + @Override + @Deactivate + protected void deactivate() { + this.essOrBatteryInverter.removeCharger(this); + this.currentListener.deactivate(); + this.powerListener.deactivate(); + this.voltageListener.deactivate(); + super.deactivate(); + } + + @Override + public void handleEvent(Event event) { + switch (event.getTopic()) { + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE: + this.calculateEnergy(); + break; + } + } + + @Override + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { + return new ModbusSlaveTable(// + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + EssDcCharger.getModbusSlaveNatureTable(accessMode)); + } + + @Override + public Timedata getTimedata() { + return this.timedata; + } + + /** + * Calculate the Energy values from ActivePower. + */ + private void calculateEnergy() { + + if (!this.getActualEnergy().isDefined()) { + this.initializeCumulatedEnergyFromTimedata(); + return; + } + + var actualPower = this.getActualPower().get(); + if (actualPower == null) { + // Not available + this.calculateActualEnergy.update(null); + } else if (actualPower > 0) { + this.calculateActualEnergy.update(actualPower); + } else { + this.calculateActualEnergy.update(0); + } + } + + /** + * Initialize cumulated energy value from from Timedata service. + */ + private void initializeCumulatedEnergyFromTimedata() { + + final var actualEnergyChannel = EssDcCharger.ChannelId.ACTUAL_ENERGY; + var timedata = this.getTimedata(); + + if (timedata == null || this.timedataQueryIsRunning) { + return; + } + + this.timedataQueryIsRunning = true; + timedata.getLatestValue(new ChannelAddress(this.id(), actualEnergyChannel.id())).thenAccept(currentEnergy -> { + + if (currentEnergy.isEmpty()) { + + final String[] stringIds = switch (this.config.mpptPort()) { + case MPPT_1 -> new String[] { "charger0", "charger1" }; + case MPPT_2 -> new String[] { "charger2", "charger3" }; + case MPPT_3 -> new String[] { "charger4", "charger5" }; + }; + + /* + * Calculate total base energy from separate string energy values + */ + timedata.getLatestValueOfNotExistingChannel(new ChannelAddress(stringIds[0], actualEnergyChannel.id()), + actualEnergyChannel.doc().getUnit()) + .thenCombine(timedata.getLatestValueOfNotExistingChannel( + new ChannelAddress(stringIds[1], actualEnergyChannel.id()), + actualEnergyChannel.doc().getUnit()), (energyString1, energyString2) -> { + return caculateEnergyFromTwoStrings(energyString1, energyString2); + }) + .thenAccept(combinedEnergy -> { + this.channel(actualEnergyChannel).setNextValue(combinedEnergy); + this.calculateActualEnergy.setBaseEnergyManually(combinedEnergy); + }); + } else { + this.channel(actualEnergyChannel) + .setNextValue(TypeUtils.getAsType(OpenemsType.LONG, currentEnergy.get())); + } + }); + } + + /** + * Calculate energy from two given strings. + * + * @param energyString1Opt energy of string 1 + * @param energyString2Opt energy of string 2 + * @return total energy + */ + public static Long caculateEnergyFromTwoStrings(Optional energyString1Opt, + Optional energyString2Opt) { + long energyString1; + long energyString2; + + try { + energyString1 = TypeUtils.getAsType(OpenemsType.LONG, energyString1Opt.get()); + } catch (Exception e) { + energyString1 = 0L; + } + try { + energyString2 = TypeUtils.getAsType(OpenemsType.LONG, energyString2Opt.get()); + } catch (Exception e) { + energyString2 = 0L; + } + return TypeUtils.sum(energyString1, energyString2); + } + + @Override + public final String debugLog() { + return "L:" + this.getActualPower().asString(); + } + + private static class GoodWeListener implements Consumer> { + + private final IntegerReadChannel goodWeChannel; + private final IntegerReadChannel mirrorChannel; + + public GoodWeListener(GoodWeChargerMpptTwoStringImpl parent, GoodWe essOrBatteryInverter, + GoodWe.ChannelId goodWeChannel, io.openems.edge.common.channel.ChannelId mirrorChannel) { + this.goodWeChannel = essOrBatteryInverter.channel(goodWeChannel); + this.mirrorChannel = parent.channel(mirrorChannel); + this.goodWeChannel.onSetNextValue(this); + } + + public void deactivate() { + this.goodWeChannel.removeOnSetNextValueCallback(this); + } + + @Override + public void accept(Value t) { + this.mirrorChannel.setNextValue(t); + } + } +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/mppt/twostring/MpptPort.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/mppt/twostring/MpptPort.java new file mode 100644 index 00000000000..e61c6dbc63d --- /dev/null +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/mppt/twostring/MpptPort.java @@ -0,0 +1,24 @@ +package io.openems.edge.goodwe.charger.mppt.twostring; + +import io.openems.edge.goodwe.common.GoodWe; + +/** + * Defines the GoodWe MPPT with two Strings. + */ +public enum MpptPort { + + MPPT_1(GoodWe.ChannelId.MPPT1_P, GoodWe.ChannelId.MPPT1_I, GoodWe.ChannelId.TWO_S_PV1_V), // + MPPT_2(GoodWe.ChannelId.MPPT2_P, GoodWe.ChannelId.MPPT2_I, GoodWe.ChannelId.TWO_S_PV3_V), // + MPPT_3(GoodWe.ChannelId.MPPT3_P, GoodWe.ChannelId.MPPT3_I, GoodWe.ChannelId.TWO_S_PV5_V); // + + public final GoodWe.ChannelId mpptPowerChannelId; + public final GoodWe.ChannelId mpptCurrentChannelId; + public final GoodWe.ChannelId mpptVoltageChannelId; + + private MpptPort(GoodWe.ChannelId mpptPowerChannelId, GoodWe.ChannelId mpptCurrentChannelId, + GoodWe.ChannelId mpptVoltageChannelId) { + this.mpptPowerChannelId = mpptPowerChannelId; + this.mpptCurrentChannelId = mpptCurrentChannelId; + this.mpptVoltageChannelId = mpptVoltageChannelId; + } +} diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/Config.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/Config.java index 14288206959..941e35ba015 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/Config.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/Config.java @@ -7,6 +7,7 @@ name = "GoodWe Charger Two-String", // description = "Implements a GoodWe PV string (for an MPPT of two strings).") +@Deprecated @interface Config { @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") String id() default "charger0"; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoString.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoString.java index adf81bff1cf..00e958794e8 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoString.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoString.java @@ -8,6 +8,7 @@ import io.openems.edge.ess.dccharger.api.EssDcCharger; import io.openems.edge.goodwe.charger.GoodWeCharger; +@Deprecated public interface GoodWeChargerTwoString extends OpenemsComponent, EssDcCharger, GoodWeCharger { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoStringImpl.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoStringImpl.java index 99d8632e405..3da7eca9664 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoStringImpl.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoStringImpl.java @@ -31,6 +31,21 @@ import io.openems.edge.timedata.api.TimedataProvider; import io.openems.edge.timedata.api.utils.CalculateEnergyFromPower; +/** + * {@link GoodWeChargerTwoStringImpl} was used to represent one string from a + * GoodWe MPPT tracker that is responsible for two strings (GoodWe ET-Systems). + * + *

+ * Possible values are: + *

  • MPPT Current + *
  • MPPT Power + *
  • String Current + *
  • String Voltage + * + *

    + * As the current values of one string are incorrect (so far DSP-Version 12, + * ARM-Version 27) the power cannot be calculated. + */ @Designate(ocd = Config.class, factory = true) @Component(// name = "GoodWe.Charger.Two-String", // @@ -40,6 +55,7 @@ @EventTopics({ // EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // }) +@Deprecated public class GoodWeChargerTwoStringImpl extends AbstractOpenemsComponent implements GoodWeChargerTwoString, EssDcCharger, GoodWeCharger, OpenemsComponent, EventHandler, TimedataProvider, ModbusSlave { diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/PvPort.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/PvPort.java index 36edaca2fcc..e5c4f5e192c 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/PvPort.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/charger/twostring/PvPort.java @@ -5,20 +5,21 @@ /** * Defines the PV-Port of a GoodWe Charger Two-String. */ +@Deprecated public enum PvPort { - PV_1(GoodWe.ChannelId.TWO_S_MPPT1_P, GoodWe.ChannelId.TWO_S_MPPT1_I, GoodWe.ChannelId.TWO_S_PV1_I, - GoodWe.ChannelId.TWO_S_PV2_I, GoodWe.ChannelId.TWO_S_PV1_V), - PV_2(GoodWe.ChannelId.TWO_S_MPPT1_P, GoodWe.ChannelId.TWO_S_MPPT1_I, GoodWe.ChannelId.TWO_S_PV2_I, - GoodWe.ChannelId.TWO_S_PV1_I, GoodWe.ChannelId.TWO_S_PV2_V), // - PV_3(GoodWe.ChannelId.TWO_S_MPPT2_P, GoodWe.ChannelId.TWO_S_MPPT2_I, GoodWe.ChannelId.TWO_S_PV3_I, - GoodWe.ChannelId.TWO_S_PV4_I, GoodWe.ChannelId.TWO_S_PV3_V), // - PV_4(GoodWe.ChannelId.TWO_S_MPPT2_P, GoodWe.ChannelId.TWO_S_MPPT2_I, GoodWe.ChannelId.TWO_S_PV4_I, - GoodWe.ChannelId.TWO_S_PV5_I, GoodWe.ChannelId.TWO_S_PV4_V), // - PV_5(GoodWe.ChannelId.TWO_S_MPPT3_P, GoodWe.ChannelId.TWO_S_MPPT3_I, GoodWe.ChannelId.TWO_S_PV5_I, - GoodWe.ChannelId.TWO_S_PV6_I, GoodWe.ChannelId.TWO_S_PV5_V), // - PV_6(GoodWe.ChannelId.TWO_S_MPPT3_P, GoodWe.ChannelId.TWO_S_MPPT3_I, GoodWe.ChannelId.TWO_S_PV6_I, - GoodWe.ChannelId.TWO_S_PV5_I, GoodWe.ChannelId.TWO_S_PV6_V); // + PV_1(GoodWe.ChannelId.MPPT1_P, GoodWe.ChannelId.MPPT1_I, GoodWe.ChannelId.TWO_S_PV1_I, GoodWe.ChannelId.TWO_S_PV2_I, + GoodWe.ChannelId.TWO_S_PV1_V), + PV_2(GoodWe.ChannelId.MPPT1_P, GoodWe.ChannelId.MPPT1_I, GoodWe.ChannelId.TWO_S_PV2_I, GoodWe.ChannelId.TWO_S_PV1_I, + GoodWe.ChannelId.TWO_S_PV2_V), // + PV_3(GoodWe.ChannelId.MPPT2_P, GoodWe.ChannelId.MPPT2_I, GoodWe.ChannelId.TWO_S_PV3_I, GoodWe.ChannelId.TWO_S_PV4_I, + GoodWe.ChannelId.TWO_S_PV3_V), // + PV_4(GoodWe.ChannelId.MPPT2_P, GoodWe.ChannelId.MPPT2_I, GoodWe.ChannelId.TWO_S_PV4_I, GoodWe.ChannelId.TWO_S_PV5_I, + GoodWe.ChannelId.TWO_S_PV4_V), // + PV_5(GoodWe.ChannelId.MPPT3_P, GoodWe.ChannelId.MPPT3_I, GoodWe.ChannelId.TWO_S_PV5_I, GoodWe.ChannelId.TWO_S_PV6_I, + GoodWe.ChannelId.TWO_S_PV5_V), // + PV_6(GoodWe.ChannelId.MPPT3_P, GoodWe.ChannelId.MPPT3_I, GoodWe.ChannelId.TWO_S_PV6_I, GoodWe.ChannelId.TWO_S_PV5_I, + GoodWe.ChannelId.TWO_S_PV6_V); // public final GoodWe.ChannelId mpptPowerChannelId; public final GoodWe.ChannelId mpptCurrentChannelId; diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/AbstractGoodWe.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/AbstractGoodWe.java index 7b0df783129..a35ad484323 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/AbstractGoodWe.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/AbstractGoodWe.java @@ -4,6 +4,7 @@ import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_1; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_2; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; import java.util.HashSet; import java.util.Map; @@ -17,11 +18,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.openems.common.exceptions.NotImplementedException; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.types.OpenemsType; -import io.openems.edge.batteryinverter.api.HybridManagedSymmetricBatteryInverter; import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; import io.openems.edge.bridge.modbus.api.ChannelMetaInfoReadAndWrite; import io.openems.edge.bridge.modbus.api.ElementToChannelConverter; @@ -55,6 +54,7 @@ import io.openems.edge.timedata.api.TimedataProvider; import io.openems.edge.timedata.api.utils.CalculateEnergyFromPower; +@SuppressWarnings("deprecation") public abstract class AbstractGoodWe extends AbstractOpenemsModbusComponent implements GoodWe, OpenemsComponent, TimedataProvider, EventHandler { @@ -100,7 +100,7 @@ protected AbstractGoodWe(// } @Override - protected final ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected final ModbusProtocol defineModbusProtocol() { var protocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(35001, Priority.LOW, // @@ -1183,7 +1183,7 @@ protected final ModbusProtocol defineModbusProtocol() throws OpenemsException { * Register 35011: GoodWeType as String (Not supported for GoodWe 20 & 30) * Register 35003: Serial number as String (Fallback for GoodWe 20 & 30) */ - ModbusUtils.readELementOnce(protocol, new StringWordElement(35011, 5), true) // + readElementOnce(protocol, ModbusUtils::retryOnNull, new StringWordElement(35011, 5)) // .thenAccept(value -> { /* @@ -1201,29 +1201,24 @@ protected final ModbusProtocol defineModbusProtocol() throws OpenemsException { /* * Evaluate GoodweType from serial number */ - try { - ModbusUtils.readELementOnce(protocol, new StringWordElement(35003, 8), true) // - .thenAccept(serialNr -> { - final var hardwareType = getGoodWeTypeFromSerialNr(serialNr); - try { - this._setGoodweType(hardwareType); - if (hardwareType == GoodWeType.FENECON_FHI_20_DAH - || hardwareType == GoodWeType.FENECON_FHI_29_9_DAH) { - this.handleMultipleStringChargers(protocol); - } - - } catch (OpenemsException e) { - this.logError(this.log, "Unable to add charger tasks for modbus protocol"); + readElementOnce(protocol, ModbusUtils::retryOnNull, new StringWordElement(35003, 8)) // + .thenAccept(serialNr -> { + final var hardwareType = getGoodWeTypeFromSerialNr(serialNr); + try { + this._setGoodweType(hardwareType); + if (hardwareType == GoodWeType.FENECON_FHI_20_DAH + || hardwareType == GoodWeType.FENECON_FHI_29_9_DAH) { + this.handleMultipleStringChargers(protocol); } - }); - } catch (OpenemsException e) { - this.logError(this.log, "Unable to read element for serial number"); - e.printStackTrace(); - } + + } catch (OpenemsException e) { + this.logError(this.log, "Unable to add charger tasks for modbus protocol"); + } + }); }); // Handles different DSP versions - ModbusUtils.readELementOnce(protocol, new UnsignedWordElement(35016), true) // + readElementOnce(protocol, ModbusUtils::retryOnNull, new UnsignedWordElement(35016)) // .thenAccept(dspVersion -> { try { @@ -1375,19 +1370,20 @@ private void handleMultipleStringChargers(ModbusProtocol protocol) throws Openem m(GoodWe.ChannelId.TWO_S_PV6_I, new UnsignedWordElement(35307), ElementToChannelConverter.SCALE_FACTOR_2), // new DummyRegisterElement(35308, 35336), - m(GoodWe.ChannelId.TWO_S_MPPT1_P, new UnsignedWordElement(35337)), - m(GoodWe.ChannelId.TWO_S_MPPT2_P, new UnsignedWordElement(35338)), - m(GoodWe.ChannelId.TWO_S_MPPT3_P, new UnsignedWordElement(35339)), + m(GoodWe.ChannelId.MPPT1_P, new UnsignedWordElement(35337)), + m(GoodWe.ChannelId.MPPT2_P, new UnsignedWordElement(35338)), + m(GoodWe.ChannelId.MPPT3_P, new UnsignedWordElement(35339)), new DummyRegisterElement(35340, 35344), // Power MPPT4 - MPPT8 - m(GoodWe.ChannelId.TWO_S_MPPT1_I, new UnsignedWordElement(35345), // + m(GoodWe.ChannelId.MPPT1_I, new UnsignedWordElement(35345), // ElementToChannelConverter.SCALE_FACTOR_2), - m(GoodWe.ChannelId.TWO_S_MPPT2_I, new UnsignedWordElement(35346), // + m(GoodWe.ChannelId.MPPT2_I, new UnsignedWordElement(35346), // ElementToChannelConverter.SCALE_FACTOR_2), - m(GoodWe.ChannelId.TWO_S_MPPT3_I, new UnsignedWordElement(35347), // + m(GoodWe.ChannelId.MPPT3_I, new UnsignedWordElement(35347), // ElementToChannelConverter.SCALE_FACTOR_2)) // ); } + // TODO: Can be removed when GoodWeChargerTwoStringImpl has been deleted private void setMultipleStringChannels() { this.chargers.stream() // @@ -1907,7 +1903,7 @@ private void handleDspVersion5(ModbusProtocol protocol) throws OpenemsException ); } - protected ModbusElement getSocModbusElement(int address) throws NotImplementedException { + protected ModbusElement getSocModbusElement(int address) { if (this instanceof HybridEss) { return m(SymmetricEss.ChannelId.SOC, new UnsignedWordElement(address), new ElementToChannelConverter( // element -> channel @@ -1923,11 +1919,9 @@ protected ModbusElement getSocModbusElement(int address) throws NotImplementedEx }, // channel -> element value -> value)); - } - if (this instanceof HybridManagedSymmetricBatteryInverter) { - return new DummyRegisterElement(address); + } else { - throw new NotImplementedException("Wrong implementation of AbstractGoodWe"); + return new DummyRegisterElement(address); } } diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/GoodWe.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/GoodWe.java index b4f33d3c86d..e5fff122fc3 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/GoodWe.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/common/GoodWe.java @@ -105,9 +105,9 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId * * MPPT1 */ - TWO_S_MPPT1_P(Doc.of(OpenemsType.INTEGER) // + MPPT1_P(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT)), - TWO_S_MPPT1_I(Doc.of(OpenemsType.INTEGER) // + MPPT1_I(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIAMPERE)), // TWO_S_PV1_V(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT)), // @@ -120,9 +120,9 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId /* * MPPT2 */ - TWO_S_MPPT2_P(Doc.of(OpenemsType.INTEGER) // + MPPT2_P(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT)), - TWO_S_MPPT2_I(Doc.of(OpenemsType.INTEGER) // + MPPT2_I(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIAMPERE)), // TWO_S_PV3_V(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT)), // @@ -135,9 +135,9 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId /* * MPPT3 */ - TWO_S_MPPT3_P(Doc.of(OpenemsType.INTEGER) // + MPPT3_P(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT)), - TWO_S_MPPT3_I(Doc.of(OpenemsType.INTEGER) // + MPPT3_I(Doc.of(OpenemsType.INTEGER) // .unit(Unit.MILLIAMPERE)), // TWO_S_PV5_V(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT)), // @@ -194,7 +194,7 @@ public static enum ChannelId implements io.openems.edge.common.channel.ChannelId V_BATTERY1(Doc.of(OpenemsType.INTEGER) // .unit(Unit.VOLT)), // I_BATTERY1(Doc.of(OpenemsType.INTEGER) // - .unit(Unit.VOLT)), // + .unit(Unit.AMPERE)), // P_BATTERY1(Doc.of(OpenemsType.INTEGER) // .unit(Unit.WATT)), // BATTERY_MODE(Doc.of(BatteryMode.values())), // diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/emergencypowermeter/GoodWeEmergencyPowerMeterImpl.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/emergencypowermeter/GoodWeEmergencyPowerMeterImpl.java index 9f0f6ed30f9..a9643334fc1 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/emergencypowermeter/GoodWeEmergencyPowerMeterImpl.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/emergencypowermeter/GoodWeEmergencyPowerMeterImpl.java @@ -100,7 +100,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // // Power of each backup up phase diff --git a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/GoodWeGridMeterImpl.java b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/GoodWeGridMeterImpl.java index 67a2a6accf3..3cb9157b12e 100644 --- a/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/GoodWeGridMeterImpl.java +++ b/io.openems.edge.goodwe/src/io/openems/edge/goodwe/gridmeter/GoodWeGridMeterImpl.java @@ -4,6 +4,7 @@ import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_1; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_2; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_MINUS_2; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; import org.osgi.service.cm.ConfigurationAdmin; import org.osgi.service.component.ComponentContext; @@ -124,7 +125,7 @@ protected void deactivate() { private final ElementToChannelConverter ignoreZeroAndInvert = IgnoreZeroConverter.from(this, INVERT); @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var protocol = new ModbusProtocol(this, // // States @@ -175,18 +176,11 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { this.ignoreZeroAndScaleFactor1))); // Handles different DSP versions - ModbusUtils.readELementOnce(protocol, new UnsignedWordElement(35016), true) // - .thenAccept(dspVersion -> { - try { - if (dspVersion >= 4 || dspVersion == 0) { - this.handleDspVersion4(protocol); - } - - } catch (OpenemsException e) { - this.logError(this.log, "Unable to add task for modbus protocol"); - e.printStackTrace(); - } - }); + readElementOnce(protocol, ModbusUtils::retryOnNull, new UnsignedWordElement(35016)).thenAccept(dspVersion -> { + if (dspVersion >= 4 || dspVersion == 0) { + this.handleDspVersion4(protocol); + } + }); switch (this.config.goodWeMeterCategory()) { case COMMERCIAL_METER -> this.handleExternalMeter(protocol); @@ -201,9 +195,8 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { * Adds Registers that are available from DSP version 4. * * @param protocol the {@link ModbusProtocol} - * @throws OpenemsException on error */ - private void handleDspVersion4(ModbusProtocol protocol) throws OpenemsException { + private void handleDspVersion4(ModbusProtocol protocol) { protocol.addTask(// new FC3ReadRegistersTask(36052, Priority.LOW, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new UnsignedWordElement(36052), @@ -220,7 +213,7 @@ private void handleDspVersion4(ModbusProtocol protocol) throws OpenemsException this.ignoreZeroAndScaleFactor2))); // } - private void handleExternalMeter(ModbusProtocol protocol) throws OpenemsException { + private void handleExternalMeter(ModbusProtocol protocol) { protocol.addTask(// new FC6WriteRegisterTask(47456, diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImplTest.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImplTest.java index 9aeacc78673..b9ddd6130e6 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImplTest.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/GoodWeBatteryInverterImplTest.java @@ -1,11 +1,16 @@ package io.openems.edge.goodwe.batteryinverter; +import static io.openems.edge.goodwe.batteryinverter.GoodWeBatteryInverterImpl.doSetBmsVoltage; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import org.junit.Test; import io.openems.common.types.ChannelAddress; import io.openems.edge.battery.api.Battery; import io.openems.edge.battery.test.DummyBattery; import io.openems.edge.bridge.modbus.test.DummyModbusBridge; +import io.openems.edge.common.channel.value.Value; import io.openems.edge.common.sum.DummySum; import io.openems.edge.common.test.AbstractComponentTest.TestCase; import io.openems.edge.common.test.ComponentTest; @@ -22,6 +27,7 @@ import io.openems.edge.goodwe.common.enums.MeterCommunicateStatus; import io.openems.edge.goodwe.common.enums.SafetyCountry; +@SuppressWarnings("deprecation") public class GoodWeBatteryInverterImplTest { private static final String MODBUS_ID = "modbus0"; @@ -73,12 +79,12 @@ public class GoodWeBatteryInverterImplTest { private static final ChannelAddress CHARGER_6_VOLTAGE = new ChannelAddress(CHARGER_6_ID, "Voltage"); private static final ChannelAddress CHARGER_6_CURRENT = new ChannelAddress(CHARGER_6_ID, "Current"); - private static final ChannelAddress TWO_S_MPPT1_P = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSMppt1P"); - private static final ChannelAddress TWO_S_MPPT1_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSMppt1I"); - private static final ChannelAddress TWO_S_MPPT2_P = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSMppt2P"); - private static final ChannelAddress TWO_S_MPPT2_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSMppt2I"); - private static final ChannelAddress TWO_S_MPPT3_P = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSMppt3P"); - private static final ChannelAddress TWO_S_MPPT3_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSMppt3I"); + private static final ChannelAddress MPPT1_P = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt1P"); + private static final ChannelAddress MPPT1_I = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt1I"); + private static final ChannelAddress MPPT2_P = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt2P"); + private static final ChannelAddress MPPT2_I = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt2I"); + private static final ChannelAddress MPPT3_P = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt3P"); + private static final ChannelAddress MPPT3_I = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt3I"); private static final ChannelAddress TWO_S_PV1_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv1I"); private static final ChannelAddress TWO_S_PV1_V = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv1V"); private static final ChannelAddress TWO_S_PV2_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv2I"); @@ -516,8 +522,8 @@ public void testTwoStringCharger() throws Exception { .setControlMode(ControlMode.SMART) // .build()) // .next(new TestCase() // - .input(TWO_S_MPPT1_I, 20) // - .input(TWO_S_MPPT1_P, 2000) // + .input(MPPT1_I, 20) // + .input(MPPT1_P, 2000) // .input(TWO_S_PV1_I, 10) // .input(TWO_S_PV2_I, 10) // .input(TWO_S_PV1_V, 240) // @@ -540,8 +546,8 @@ public void testTwoStringCharger() throws Exception { // Chargers with different current values .next(new TestCase() // - .input(TWO_S_MPPT1_I, 20) // - .input(TWO_S_MPPT1_P, 2000) // + .input(MPPT1_I, 20) // + .input(MPPT1_P, 2000) // .input(TWO_S_PV1_I, 5) // .input(TWO_S_PV2_I, 15) // .output(CHARGER_ACTUAL_POWER, 1000) // @@ -551,8 +557,8 @@ public void testTwoStringCharger() throws Exception { .output(CHARGER_2_ACTUAL_POWER, 1500)) // .next(new TestCase() // - .input(TWO_S_MPPT1_I, 20) // - .input(TWO_S_MPPT1_P, 2000) // + .input(MPPT1_I, 20) // + .input(MPPT1_P, 2000) // .input(TWO_S_PV1_I, 20) // .input(TWO_S_PV2_I, 0) // .output(CHARGER_ACTUAL_POWER, 500) // @@ -588,8 +594,8 @@ public void testTwoStringCharger() throws Exception { .setControlMode(ControlMode.SMART) // .build()) // .next(new TestCase() // - .input(TWO_S_MPPT2_I, 20) // - .input(TWO_S_MPPT2_P, 2000) // + .input(MPPT2_I, 20) // + .input(MPPT2_P, 2000) // .input(TWO_S_PV3_I, 10) // .input(TWO_S_PV4_I, 10) // .input(TWO_S_PV3_V, 240) // @@ -612,8 +618,8 @@ public void testTwoStringCharger() throws Exception { // Chargers with different current values .next(new TestCase() // - .input(TWO_S_MPPT2_I, 20) // - .input(TWO_S_MPPT2_P, 2000) // + .input(MPPT2_I, 20) // + .input(MPPT2_P, 2000) // .input(TWO_S_PV3_I, 5) // .input(TWO_S_PV4_I, 15) // .output(CHARGER_3_ACTUAL_POWER, 1000) // @@ -623,8 +629,8 @@ public void testTwoStringCharger() throws Exception { .output(CHARGER_4_ACTUAL_POWER, 1500)) // .next(new TestCase() // - .input(TWO_S_MPPT2_I, 20) // - .input(TWO_S_MPPT2_P, 2000) // + .input(MPPT2_I, 20) // + .input(MPPT2_P, 2000) // .input(TWO_S_PV3_I, 20) // .input(TWO_S_PV4_I, 0) // .output(CHARGER_3_ACTUAL_POWER, 500) // @@ -660,8 +666,8 @@ public void testTwoStringCharger() throws Exception { .setControlMode(ControlMode.SMART) // .build()) // .next(new TestCase() // - .input(TWO_S_MPPT3_I, 20) // - .input(TWO_S_MPPT3_P, 2000) // + .input(MPPT3_I, 20) // + .input(MPPT3_P, 2000) // .input(TWO_S_PV5_I, 10) // .input(TWO_S_PV6_I, 10) // .input(TWO_S_PV5_V, 240) // @@ -684,8 +690,8 @@ public void testTwoStringCharger() throws Exception { // Chargers with different current values .next(new TestCase() // - .input(TWO_S_MPPT3_I, 20) // - .input(TWO_S_MPPT3_P, 2000) // + .input(MPPT3_I, 20) // + .input(MPPT3_P, 2000) // .input(TWO_S_PV5_I, 5) // .input(TWO_S_PV6_I, 15) // .output(CHARGER_5_ACTUAL_POWER, 1000) // @@ -695,8 +701,8 @@ public void testTwoStringCharger() throws Exception { .output(CHARGER_6_ACTUAL_POWER, 1500)) // .next(new TestCase() // - .input(TWO_S_MPPT3_I, 20) // - .input(TWO_S_MPPT3_P, 2000) // + .input(MPPT3_I, 20) // + .input(MPPT3_P, 2000) // .input(TWO_S_PV5_I, 20) // .input(TWO_S_PV6_I, 0) // .output(CHARGER_5_ACTUAL_POWER, 500) // @@ -706,4 +712,37 @@ public void testTwoStringCharger() throws Exception { .output(CHARGER_6_ACTUAL_POWER, 0) // ); } + + @Test + public void testDoSetBmsVoltage() { + final var battery = new DummyBattery("battery0"); + final var bmsChargeMaxVoltage = new Value(null, 123); + final var bmsDischargeMinVoltage = new Value(null, 456); + + // No battery values + assertFalse(doSetBmsVoltage(battery, bmsChargeMaxVoltage, 1, bmsDischargeMinVoltage, 1)); + battery // + .withChargeMaxCurrent(234) // + .withDischargeMaxCurrent(234); + + // Battery full + battery // + .withChargeMaxCurrent(0); // + assertFalse(doSetBmsVoltage(battery, bmsChargeMaxVoltage, 1, bmsDischargeMinVoltage, 1)); + + // Battery empty + battery // + .withDischargeMaxCurrent(0); // + assertFalse(doSetBmsVoltage(battery, bmsChargeMaxVoltage, 1, bmsDischargeMinVoltage, 1)); + + // Values are already set + battery // + .withChargeMaxCurrent(234) // + .withDischargeMaxCurrent(234); + assertFalse(doSetBmsVoltage(battery, bmsChargeMaxVoltage, 123, bmsDischargeMinVoltage, 456)); + + // Values should be updated + assertTrue(doSetBmsVoltage(battery, bmsChargeMaxVoltage, 1, bmsDischargeMinVoltage, 456)); + assertTrue(doSetBmsVoltage(battery, bmsChargeMaxVoltage, 123, bmsDischargeMinVoltage, 1)); + } } diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/MyConfig.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/MyConfig.java index 35f50b2dda5..5d5a2db41ee 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/MyConfig.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/batteryinverter/MyConfig.java @@ -10,7 +10,7 @@ @SuppressWarnings("all") public class MyConfig extends AbstractComponentConfig implements Config { - protected static class Builder { + public static class Builder { private String id; private ControlMode controlMode; private String modbusId; diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/mppt/twostring/GoodWeChargerMpptTwoStringImplTest.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/mppt/twostring/GoodWeChargerMpptTwoStringImplTest.java new file mode 100644 index 00000000000..9ba2a25098a --- /dev/null +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/mppt/twostring/GoodWeChargerMpptTwoStringImplTest.java @@ -0,0 +1,190 @@ +package io.openems.edge.goodwe.charger.mppt.twostring; + +import org.junit.Test; + +import io.openems.common.types.ChannelAddress; +import io.openems.edge.battery.api.Battery; +import io.openems.edge.battery.test.DummyBattery; +import io.openems.edge.bridge.modbus.test.DummyModbusBridge; +import io.openems.edge.common.sum.DummySum; +import io.openems.edge.common.test.AbstractComponentTest.TestCase; +import io.openems.edge.common.test.ComponentTest; +import io.openems.edge.common.test.DummyConfigurationAdmin; +import io.openems.edge.ess.test.DummyPower; +import io.openems.edge.goodwe.GoodWeConstants; +import io.openems.edge.goodwe.batteryinverter.GoodWeBatteryInverterImpl; +import io.openems.edge.goodwe.common.enums.ControlMode; +import io.openems.edge.goodwe.common.enums.EnableDisable; +import io.openems.edge.goodwe.common.enums.FeedInPowerSettings; +import io.openems.edge.goodwe.common.enums.SafetyCountry; + +public class GoodWeChargerMpptTwoStringImplTest { + + private static final String MODBUS_ID = "modbus0"; + private static final String BATTERY_ID = "battery0"; + private static final String CHARGER_1_ID = "charger0"; + private static final String CHARGER_2_ID = "charger1"; + private static final String CHARGER_3_ID = "charger2"; + private static final String BATTERY_INVERTER_ID = "batteryInverter0"; + + private static final Battery BATTERY = new DummyBattery(BATTERY_ID); + + private static final ChannelAddress MPPT1_P = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt1P"); + private static final ChannelAddress MPPT1_I = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt1I"); + private static final ChannelAddress MPPT2_P = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt2P"); + private static final ChannelAddress MPPT2_I = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt2I"); + private static final ChannelAddress MPPT3_P = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt3P"); + private static final ChannelAddress MPPT3_I = new ChannelAddress(BATTERY_INVERTER_ID, "Mppt3I"); + private static final ChannelAddress TWO_S_PV1_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv1I"); + private static final ChannelAddress TWO_S_PV1_V = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv1V"); + private static final ChannelAddress TWO_S_PV2_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv2I"); + private static final ChannelAddress TWO_S_PV2_V = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv2V"); + private static final ChannelAddress TWO_S_PV3_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv3I"); + private static final ChannelAddress TWO_S_PV3_V = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv3V"); + private static final ChannelAddress TWO_S_PV4_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv4I"); + private static final ChannelAddress TWO_S_PV4_V = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv4V"); + private static final ChannelAddress TWO_S_PV5_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv5I"); + private static final ChannelAddress TWO_S_PV5_V = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv5V"); + private static final ChannelAddress TWO_S_PV6_I = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv6I"); + private static final ChannelAddress TWO_S_PV6_V = new ChannelAddress(BATTERY_INVERTER_ID, "TwoSPv6V"); + private static final ChannelAddress CHARGER_1_ACTUAL_POWER = new ChannelAddress(CHARGER_1_ID, "ActualPower"); + private static final ChannelAddress CHARGER_1_VOLTAGE = new ChannelAddress(CHARGER_1_ID, "Voltage"); + private static final ChannelAddress CHARGER_1_CURRENT = new ChannelAddress(CHARGER_1_ID, "Current"); + private static final ChannelAddress CHARGER_2_ACTUAL_POWER = new ChannelAddress(CHARGER_2_ID, "ActualPower"); + private static final ChannelAddress CHARGER_2_VOLTAGE = new ChannelAddress(CHARGER_2_ID, "Voltage"); + private static final ChannelAddress CHARGER_2_CURRENT = new ChannelAddress(CHARGER_2_ID, "Current"); + private static final ChannelAddress CHARGER_3_ACTUAL_POWER = new ChannelAddress(CHARGER_3_ID, "ActualPower"); + private static final ChannelAddress CHARGER_3_VOLTAGE = new ChannelAddress(CHARGER_3_ID, "Voltage"); + private static final ChannelAddress CHARGER_3_CURRENT = new ChannelAddress(CHARGER_3_ID, "Current"); + + @Test + public void test() throws Exception { + var ess = new GoodWeBatteryInverterImpl(); + var charger1 = new GoodWeChargerMpptTwoStringImpl(); + var charger2 = new GoodWeChargerMpptTwoStringImpl(); + var charger3 = new GoodWeChargerMpptTwoStringImpl(); + + new ComponentTest(charger1) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("essOrBatteryInverter", ess) // + .activate(MyConfig.create() // + .setId(CHARGER_1_ID) // + .setBatteryInverterId(BATTERY_INVERTER_ID) // + .setMpptPort(MpptPort.MPPT_1) // + .build()); + + new ComponentTest(charger2) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("essOrBatteryInverter", ess) // + .activate(MyConfig.create() // + .setId(CHARGER_2_ID) // + .setBatteryInverterId(BATTERY_INVERTER_ID) // + .setMpptPort(MpptPort.MPPT_2) // + .build()); + + new ComponentTest(charger3) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("essOrBatteryInverter", ess) // + .activate(MyConfig.create() // + .setId(CHARGER_3_ID) // + .setBatteryInverterId(BATTERY_INVERTER_ID) // + .setMpptPort(MpptPort.MPPT_3) // + .build()); + + ess.addCharger(charger1); + ess.addCharger(charger2); + ess.addCharger(charger3); + + new ComponentTest(ess) // + .addReference("power", new DummyPower()) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // + .addReference("sum", new DummySum()) // + .addComponent(charger1) // + .addComponent(charger2) // + .addComponent(charger3) // + .addComponent(BATTERY) // + .activate(io.openems.edge.goodwe.batteryinverter.MyConfig.create() // + .setId(BATTERY_INVERTER_ID) // + .setModbusId(MODBUS_ID) // + .setModbusUnitId(GoodWeConstants.DEFAULT_UNIT_ID) // + .setSafetyCountry(SafetyCountry.GERMANY) // + .setMpptForShadowEnable(EnableDisable.ENABLE) // + .setBackupEnable(EnableDisable.ENABLE) // + .setFeedPowerEnable(EnableDisable.ENABLE) // + .setFeedPowerPara(3000) // + .setFeedInPowerSettings(FeedInPowerSettings.PU_ENABLE_CURVE) // + .setControlMode(ControlMode.SMART) // + .build()) // + .next(new TestCase() // + .input(MPPT1_I, 20) // + .input(MPPT1_P, 2000) // + .input(TWO_S_PV1_I, 10) // + .input(TWO_S_PV2_I, 10) // + .input(TWO_S_PV1_V, 240) // + .input(TWO_S_PV2_V, 240)) // + // Values applied in the next cycle + .next(new TestCase() // + .output(CHARGER_1_ACTUAL_POWER, 2000) // + .output(CHARGER_1_CURRENT, 20) // + .output(CHARGER_1_VOLTAGE, 240) // + .output(CHARGER_2_ACTUAL_POWER, null) // + .output(CHARGER_2_CURRENT, null) // + .output(CHARGER_2_VOLTAGE, null) // + .output(CHARGER_3_ACTUAL_POWER, null) // + .output(CHARGER_3_CURRENT, null) // + .output(CHARGER_3_VOLTAGE, null) // + ) // + + // Chargers with different current values + .next(new TestCase() // + .input(MPPT1_I, 20) // + .input(MPPT1_P, 3000) // + .input(TWO_S_PV1_I, 5) // + .input(TWO_S_PV2_I, 15) // + .input(TWO_S_PV1_V, 250) // + .input(TWO_S_PV2_V, 250)) // + .next(new TestCase() // + .output(CHARGER_1_ACTUAL_POWER, 3000) // + .output(CHARGER_1_CURRENT, 20) // + .output(CHARGER_1_VOLTAGE, 250) // + .output(CHARGER_2_ACTUAL_POWER, null) // + .output(CHARGER_2_CURRENT, null) // + .output(CHARGER_2_VOLTAGE, null). // + output(CHARGER_3_ACTUAL_POWER, null) // + .output(CHARGER_3_CURRENT, null) // + .output(CHARGER_3_VOLTAGE, null) // + ) + + .next(new TestCase() // + .input(MPPT1_I, 20) // + .input(MPPT1_P, 2000) // + .input(MPPT2_I, 30) // + .input(MPPT2_P, 3000) // + .input(MPPT3_I, 40) // + .input(MPPT3_P, 4000) // + .input(TWO_S_PV1_I, 10) // + .input(TWO_S_PV1_V, 250) // + .input(TWO_S_PV2_I, 10) // + .input(TWO_S_PV2_V, 250) // + .input(TWO_S_PV3_I, 15) // + .input(TWO_S_PV3_V, 280) // + .input(TWO_S_PV4_I, 15) // + .input(TWO_S_PV4_V, 280) // + .input(TWO_S_PV5_I, 20) // + .input(TWO_S_PV5_V, 299) // + .input(TWO_S_PV6_I, 20) // + .input(TWO_S_PV6_V, 299)) // + .next(new TestCase() // + .output(CHARGER_1_ACTUAL_POWER, 2000) // + .output(CHARGER_1_CURRENT, 20) // + .output(CHARGER_1_VOLTAGE, 250) // + .output(CHARGER_2_ACTUAL_POWER, 3000) // + .output(CHARGER_2_CURRENT, 30) // + .output(CHARGER_2_VOLTAGE, 280). // + output(CHARGER_3_ACTUAL_POWER, 4000) // + .output(CHARGER_3_CURRENT, 40) // + .output(CHARGER_3_VOLTAGE, 299) // + ); + } +} diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/mppt/twostring/MyConfig.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/mppt/twostring/MyConfig.java new file mode 100644 index 00000000000..79d7f0b1623 --- /dev/null +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/mppt/twostring/MyConfig.java @@ -0,0 +1,72 @@ +package io.openems.edge.goodwe.charger.mppt.twostring; + +import io.openems.common.test.AbstractComponentConfig; +import io.openems.common.utils.ConfigUtils; + +@SuppressWarnings("all") +public class MyConfig extends AbstractComponentConfig implements Config { + + public static class Builder { + private String id; + private MpptPort mpptPort; + private String essOrBatteryInverter; + + private Builder() { + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setBatteryInverterId(String essOrBatteryInverter) { + this.essOrBatteryInverter = essOrBatteryInverter; + return this; + } + + public Builder setMpptPort(MpptPort mpptPort) { + this.mpptPort = mpptPort; + return this; + } + + /** + * Builds the Config. + * + * @return the Config + */ + public MyConfig build() { + return new MyConfig(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new Builder(); + } + + private final Builder builder; + + private MyConfig(Builder builder) { + super(Config.class, builder.id); + this.builder = builder; + } + + @Override + public String essOrBatteryInverter_id() { + return this.builder.essOrBatteryInverter; + } + + @Override + public String essOrBatteryInverter_target() { + return ConfigUtils.generateReferenceTargetFilter(this.id(), this.essOrBatteryInverter_id()); + } + + @Override + public MpptPort mpptPort() { + return this.builder.mpptPort; + } +} diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/mppt/twostring/TestStatic.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/mppt/twostring/TestStatic.java new file mode 100644 index 00000000000..b1149ad7c64 --- /dev/null +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/mppt/twostring/TestStatic.java @@ -0,0 +1,28 @@ +package io.openems.edge.goodwe.charger.mppt.twostring; + +import static org.junit.Assert.assertEquals; + +import java.util.Optional; + +import org.junit.Test; + +public class TestStatic { + + @Test + public void testCaculateEnergyFromTwoStrings() { + Optional string1 = Optional.of(40_000L); + Optional string2 = Optional.of(90_000L); + Optional string3 = Optional.empty(); + Optional string4 = Optional.of(0); + + var result = GoodWeChargerMpptTwoStringImpl.caculateEnergyFromTwoStrings(string1, string2); + assertEquals((long) result, 130_000L); + + var result2 = GoodWeChargerMpptTwoStringImpl.caculateEnergyFromTwoStrings(string3, string4); + assertEquals((long) result2, 0); + + var result3 = GoodWeChargerMpptTwoStringImpl.caculateEnergyFromTwoStrings(string1, string3); + assertEquals((long) result3, 40_000L); + } + +} diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoStringImplTest.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoStringImplTest.java index 75841a8ca30..3d2e5340e78 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoStringImplTest.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/twostring/GoodWeChargerTwoStringImplTest.java @@ -11,6 +11,7 @@ public class GoodWeChargerTwoStringImplTest { private static final String ESS_ID = "ess0"; private static final String CHARGER_ID = "charger0"; + @SuppressWarnings("deprecation") @Test public void test() throws Exception { new ComponentTest(new GoodWeChargerTwoStringImpl()) // diff --git a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/twostring/RuleOfThreeTest.java b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/twostring/RuleOfThreeTest.java index 5298ac5f23b..cfe59e9bb6a 100644 --- a/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/twostring/RuleOfThreeTest.java +++ b/io.openems.edge.goodwe/test/io/openems/edge/goodwe/charger/twostring/RuleOfThreeTest.java @@ -7,6 +7,7 @@ import org.junit.Test; +@SuppressWarnings("deprecation") public class RuleOfThreeTest { @Test diff --git a/io.openems.edge.io.filipowski/src/io/openems/edge/io/filipowski/analog/mr/IoFilipowskiMrAo1Impl.java b/io.openems.edge.io.filipowski/src/io/openems/edge/io/filipowski/analog/mr/IoFilipowskiMrAo1Impl.java index ea29a1b0e6a..6f6a08401a4 100644 --- a/io.openems.edge.io.filipowski/src/io/openems/edge/io/filipowski/analog/mr/IoFilipowskiMrAo1Impl.java +++ b/io.openems.edge.io.filipowski/src/io/openems/edge/io/filipowski/analog/mr/IoFilipowskiMrAo1Impl.java @@ -79,7 +79,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var address = this.config.analogOutput().startAddress; return new ModbusProtocol(this, // diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/IoKmtronicRelay8PortImpl.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/IoKmtronicRelay8PortImpl.java index f6d0994bd0b..74d57edee6c 100644 --- a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/IoKmtronicRelay8PortImpl.java +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/eight/IoKmtronicRelay8PortImpl.java @@ -66,7 +66,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // /* * For Read: Read Coils diff --git a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/IoKmtronicRelay4PortImpl.java b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/IoKmtronicRelay4PortImpl.java index 63f142ffac9..431ba55d1b4 100644 --- a/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/IoKmtronicRelay4PortImpl.java +++ b/io.openems.edge.io.kmtronic/src/io/openems/edge/io/kmtronic/four/IoKmtronicRelay4PortImpl.java @@ -66,7 +66,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // /* * For Read: Read Coils diff --git a/io.openems.edge.io.shelly/readme.adoc b/io.openems.edge.io.shelly/readme.adoc index 69c6b6c7d64..16eed94af93 100644 --- a/io.openems.edge.io.shelly/readme.adoc +++ b/io.openems.edge.io.shelly/readme.adoc @@ -3,10 +3,9 @@ This bundle implements Shelly WiFi Relay Switches. Compatible with -- https://shelly.cloud/products/shelly-25-smart-home-automation-relay/[Shelly 2.5] -- https://shelly.cloud/products/shelly-plug-s-smart-home-automation-device/[Shelly Plug S] - -Implemented Natures -- DigitalOutput +- https://www.shelly.com/de/products/shop/1xs25[Shelly 2.5] +- https://www.shelly.com/en/products/shop/shelly-3-em[Shelly 3EM] +- Shelly Plug S +- https://www.shelly.com/de/products/shop/shelly-plus-plug-s-1[Shelly Plus Plug S] https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.io.shelly[Source Code icon:github[]] \ No newline at end of file diff --git a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/common/Utils.java b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/common/Utils.java new file mode 100644 index 00000000000..14595f04ea6 --- /dev/null +++ b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/common/Utils.java @@ -0,0 +1,29 @@ +package io.openems.edge.io.shelly.common; + +import io.openems.edge.common.channel.Channel; +import io.openems.edge.common.component.OpenemsComponent; + +public class Utils { + + private Utils() { + } + + /** + * Generates a standard Debug-Log string for Shellys with one relay and power + * meter. + * + * @param relayChannel the Relay-Channel + * @param activePowerChannel the ActivePower-Channel + * @return suitable for {@link OpenemsComponent#debugLog()} + */ + public static String generateDebugLog(Channel relayChannel, Channel activePowerChannel) { + var b = new StringBuilder(); + relayChannel.value().asOptional().ifPresentOrElse(// + v -> b.append(v ? "On" : "Off"), // + () -> b.append("Unknown")); + b.append("|"); + b.append(activePowerChannel.value().asString()); + return b.toString(); + } + +} diff --git a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly25/IoShelly25Impl.java b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly25/IoShelly25Impl.java index d802358f7db..aad007289db 100644 --- a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly25/IoShelly25Impl.java +++ b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly25/IoShelly25Impl.java @@ -22,7 +22,6 @@ import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; import io.openems.edge.io.api.DigitalOutput; -import io.openems.edge.io.shelly.common.ShellyApi; @Designate(ocd = Config.class, factory = true) @Component(// diff --git a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/common/ShellyApi.java b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly25/ShellyApi.java similarity index 97% rename from io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/common/ShellyApi.java rename to io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly25/ShellyApi.java index 7c24c80703e..6b3b8a3db6f 100644 --- a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/common/ShellyApi.java +++ b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly25/ShellyApi.java @@ -1,4 +1,4 @@ -package io.openems.edge.io.shelly.common; +package io.openems.edge.io.shelly.shelly25; import java.io.BufferedReader; import java.io.IOException; @@ -13,6 +13,7 @@ import io.openems.common.exceptions.OpenemsException; import io.openems.common.utils.JsonUtils; +// TODO replace with HttpBridge /** * Implements the local Shelly REST Api. * diff --git a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly3em/IoShelly3EmImpl.java b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly3em/IoShelly3EmImpl.java index 5d899b8faba..1cb2c8d1f1e 100644 --- a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly3em/IoShelly3EmImpl.java +++ b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shelly3em/IoShelly3EmImpl.java @@ -4,6 +4,7 @@ import static io.openems.common.utils.JsonUtils.getAsFloat; import static io.openems.common.utils.JsonUtils.getAsJsonArray; import static io.openems.common.utils.JsonUtils.getAsJsonObject; +import static io.openems.edge.io.shelly.common.Utils.generateDebugLog; import static java.lang.Math.round; import java.util.Objects; @@ -109,17 +110,7 @@ public BooleanWriteChannel[] digitalOutputChannels() { @Override public String debugLog() { - var b = new StringBuilder(); - var valueOpt = this.getRelayChannel().value().asOptional(); - b.append(valueOpt.isPresent() // - ? (valueOpt.get() // - ? "ON" // - : "OFF") // - : "Unknown"); - b.append("|"); - b.append(this.getActivePowerChannel().value().asString()); - - return b.toString(); + return generateDebugLog(this.getRelayChannel(), this.getActivePowerChannel()); } @Override diff --git a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplug/IoShellyPlugImpl.java b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplug/IoShellyPlugImpl.java index 03bbb6a9982..a883becf42c 100644 --- a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplug/IoShellyPlugImpl.java +++ b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplug/IoShellyPlugImpl.java @@ -1,5 +1,7 @@ package io.openems.edge.io.shelly.shellyplug; +import static io.openems.edge.io.shelly.common.Utils.generateDebugLog; + import java.util.Objects; import org.osgi.service.component.ComponentContext; @@ -99,16 +101,7 @@ public BooleanWriteChannel[] digitalOutputChannels() { @Override public String debugLog() { - var b = new StringBuilder(); - var valueOpt = this.getRelayChannel().value().asOptional(); - if (valueOpt.isPresent()) { - b.append(valueOpt.get() ? "On" : "Off"); - } else { - b.append("Unknown"); - } - b.append("|"); - b.append(this.getActivePowerChannel().value().asString()); - return b.toString(); + return generateDebugLog(this.getRelayChannel(), this.getActivePowerChannel()); } @Override diff --git a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplusplugs/Config.java b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplusplugs/Config.java new file mode 100644 index 00000000000..39904456e3f --- /dev/null +++ b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplusplugs/Config.java @@ -0,0 +1,33 @@ +package io.openems.edge.io.shelly.shellyplusplugs; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; + +@ObjectClassDefinition(// + name = "IO Shelly Plus Plug S", // + description = "Implements the Shelly Plus Plug S") +@interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") + String id() default "io0"; + + @AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID") + String alias() default ""; + + @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") + boolean enabled() default true; + + @AttributeDefinition(name = "Phase", description = "Which Phase is this Shelly Plug connected to?") + SinglePhase phase() default SinglePhase.L1; + + @AttributeDefinition(name = "IP-Address", description = "The IP address of the Shelly device.") + String ip(); + + @AttributeDefinition(name = "Meter-Type", description = "What is measured by this Meter?") + MeterType type() default MeterType.CONSUMPTION_METERED; + + String webconsole_configurationFactory_nameHint() default "IO Shelly Plus Plug S [{id}]"; +} \ No newline at end of file diff --git a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplusplugs/IoShellyPlusPlugs.java b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplusplugs/IoShellyPlusPlugs.java new file mode 100644 index 00000000000..d0224d1e99c --- /dev/null +++ b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplusplugs/IoShellyPlusPlugs.java @@ -0,0 +1,144 @@ +package io.openems.edge.io.shelly.shellyplusplugs; + +import org.osgi.service.event.EventHandler; + +import io.openems.common.channel.AccessMode; +import io.openems.common.channel.Level; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.types.OpenemsType; +import io.openems.edge.common.channel.BooleanDoc; +import io.openems.edge.common.channel.BooleanWriteChannel; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.channel.StateChannel; +import io.openems.edge.common.channel.value.Value; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.io.api.DigitalOutput; +import io.openems.edge.meter.api.ElectricityMeter; +import io.openems.edge.meter.api.SinglePhaseMeter; + +public interface IoShellyPlusPlugs + extends DigitalOutput, SinglePhaseMeter, ElectricityMeter, OpenemsComponent, EventHandler { + + public static enum ChannelId implements io.openems.edge.common.channel.ChannelId { + /** + * Holds writes to Relay Output for debugging. + * + *
      + *
    • Interface: ShellyPlug + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + DEBUG_RELAY(Doc.of(OpenemsType.BOOLEAN)), // + /** + * Relay Output. + * + *
      + *
    • Interface: ShellyPlug + *
    • Type: Boolean + *
    • Range: On/Off + *
    + */ + RELAY(new BooleanDoc() // + .accessMode(AccessMode.READ_WRITE) // + .onChannelSetNextWriteMirrorToDebugChannel(ChannelId.DEBUG_RELAY)), + /** + * Indicates if an update is available. + * + *
      + *
    • Interface: ShellyPlug + *
    • Type: Boolean + *
    • Level: INFO + *
    + */ + HAS_UPDATE(Doc.of(Level.INFO) // + .text("A new Firmware Update is available.")), + /** + * Slave Communication Failed Fault. + * + *
      + *
    • Interface: ShellyPlug + *
    • Type: State + *
    + */ + SLAVE_COMMUNICATION_FAILED(Doc.of(Level.FAULT)); // + + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + @Override + public Doc doc() { + return this.doc; + } + } + + /** + * Gets the Channel for {@link ChannelId#RELAY}. + * + * @return the Channel + */ + public default BooleanWriteChannel getRelayChannel() { + return this.channel(ChannelId.RELAY); + } + + /** + * Gets the Relay Output 1. See {@link ChannelId#RELAY}. + * + * @return the Channel {@link Value} + */ + public default Value getRelay() { + return this.getRelayChannel().value(); + } + + /** + * Internal method to set the 'nextValue' on {@link ChannelId#RELAY} Channel. + * + * @param value the next value + */ + public default void _setRelay(Boolean value) { + this.getRelayChannel().setNextValue(value); + } + + /** + * Sets the Relay Output. See {@link ChannelId#RELAY}. + * + * @param value the next write value + * @throws OpenemsNamedException on error + */ + public default void setRelay(boolean value) throws OpenemsNamedException { + this.getRelayChannel().setNextWriteValue(value); + } + + /** + * Gets the Channel for {@link ChannelId#SLAVE_COMMUNICATION_FAILED}. + * + * @return the Channel + */ + public default StateChannel getSlaveCommunicationFailedChannel() { + return this.channel(ChannelId.SLAVE_COMMUNICATION_FAILED); + } + + /** + * Gets the Slave Communication Failed State. See + * {@link ChannelId#SLAVE_COMMUNICATION_FAILED}. + * + * @return the Channel {@link Value} + */ + public default Value getSlaveCommunicationFailed() { + return this.getSlaveCommunicationFailedChannel().value(); + } + + /** + * Internal method to set the 'nextValue' on + * {@link ChannelId#SLAVE_COMMUNICATION_FAILED} Channel. + * + * @param value the next value + */ + public default void _setSlaveCommunicationFailed(boolean value) { + this.getSlaveCommunicationFailedChannel().setNextValue(value); + } + +} \ No newline at end of file diff --git a/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplusplugs/IoShellyPlusPlugsImpl.java b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplusplugs/IoShellyPlusPlugsImpl.java new file mode 100644 index 00000000000..fd441091fa7 --- /dev/null +++ b/io.openems.edge.io.shelly/src/io/openems/edge/io/shelly/shellyplusplugs/IoShellyPlusPlugsImpl.java @@ -0,0 +1,237 @@ +package io.openems.edge.io.shelly.shellyplusplugs; + +import static io.openems.common.utils.JsonUtils.getAsBoolean; +import static io.openems.common.utils.JsonUtils.getAsFloat; +import static io.openems.common.utils.JsonUtils.getAsJsonObject; +import static io.openems.edge.io.shelly.common.Utils.generateDebugLog; +import static java.lang.Math.round; + +import java.util.Objects; + +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventHandler; +import org.osgi.service.event.propertytypes.EventTopics; +import org.osgi.service.metatype.annotations.Designate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.gson.JsonElement; + +import io.openems.edge.bridge.http.api.BridgeHttp; +import io.openems.edge.bridge.http.api.BridgeHttpFactory; +import io.openems.edge.common.channel.BooleanWriteChannel; +import io.openems.edge.common.component.AbstractOpenemsComponent; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.event.EdgeEventConstants; +import io.openems.edge.io.api.DigitalOutput; +import io.openems.edge.meter.api.ElectricityMeter; +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; +import io.openems.edge.meter.api.SinglePhaseMeter; +import io.openems.edge.timedata.api.Timedata; +import io.openems.edge.timedata.api.TimedataProvider; +import io.openems.edge.timedata.api.utils.CalculateEnergyFromPower; + +@Designate(ocd = Config.class, factory = true) +@Component(// + name = "IO.Shelly.Plus.PlugS", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE// +) +@EventTopics({ // + EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE, // + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // +}) +public class IoShellyPlusPlugsImpl extends AbstractOpenemsComponent implements IoShellyPlusPlugs, DigitalOutput, + SinglePhaseMeter, ElectricityMeter, OpenemsComponent, TimedataProvider, EventHandler { + + private final CalculateEnergyFromPower calculateProductionEnergy = new CalculateEnergyFromPower(this, + ElectricityMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY); + private final CalculateEnergyFromPower calculateConsumptionEnergy = new CalculateEnergyFromPower(this, + ElectricityMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY); + + private final Logger log = LoggerFactory.getLogger(IoShellyPlusPlugsImpl.class); + private final BooleanWriteChannel[] digitalOutputChannels; + + private MeterType meterType = null; + private SinglePhase phase = null; + private String baseUrl; + + @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) + private volatile Timedata timedata; + + @Reference(cardinality = ReferenceCardinality.MANDATORY) + private BridgeHttpFactory httpBridgeFactory; + private BridgeHttp httpBridge; + + public IoShellyPlusPlugsImpl() { + super(// + OpenemsComponent.ChannelId.values(), // + ElectricityMeter.ChannelId.values(), // + DigitalOutput.ChannelId.values(), // + IoShellyPlusPlugs.ChannelId.values() // + ); + this.digitalOutputChannels = new BooleanWriteChannel[] { // + this.channel(IoShellyPlusPlugs.ChannelId.RELAY) // + }; + + SinglePhaseMeter.calculateSinglePhaseFromActivePower(this); + SinglePhaseMeter.calculateSinglePhaseFromCurrent(this); + SinglePhaseMeter.calculateSinglePhaseFromVoltage(this); + } + + @Activate + protected void activate(ComponentContext context, Config config) { + super.activate(context, config.id(), config.alias(), config.enabled()); + this.meterType = config.type(); + this.phase = config.phase(); + this.baseUrl = "http://" + config.ip(); + this.httpBridge = this.httpBridgeFactory.get(); + + if (!this.isEnabled()) { + return; + } + + this.httpBridge.subscribeJsonEveryCycle(this.baseUrl + "/rpc/Shelly.GetStatus", this::processHttpResult); + } + + @Override + @Deactivate + protected void deactivate() { + if (this.httpBridge != null) { + this.httpBridgeFactory.unget(this.httpBridge); + this.httpBridge = null; + } + super.deactivate(); + } + + @Override + public BooleanWriteChannel[] digitalOutputChannels() { + return this.digitalOutputChannels; + } + + @Override + public String debugLog() { + return generateDebugLog(this.getRelayChannel(), this.getActivePowerChannel()); + } + + @Override + public void handleEvent(Event event) { + if (!this.isEnabled()) { + return; + } + + switch (event.getTopic()) { + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // + -> this.calculateEnergy(); + case EdgeEventConstants.TOPIC_CYCLE_EXECUTE_WRITE // + -> this.executeWrite(); + } + } + + private void processHttpResult(JsonElement result, Throwable error) { + this._setSlaveCommunicationFailed(result == null); + + Boolean relayStatus = null; + Boolean updatesAvailable = false; + Integer activePower = null; + Integer current = null; + Integer voltage = null; + + if (error != null) { + this.logWarn(this.log, error.getMessage()); + + } else { + try { + var response = getAsJsonObject(result); + var sysInfo = getAsJsonObject(response, "sys"); + var update = getAsJsonObject(sysInfo, "available_updates"); + updatesAvailable = update != null && !update.entrySet().isEmpty(); + + var relays = getAsJsonObject(response, "switch:0"); + activePower = round(getAsFloat(relays, "apower")); + current = round(getAsFloat(relays, "current") * 1000); + voltage = round(getAsFloat(relays, "voltage") * 1000); + relayStatus = getAsBoolean(relays, "output"); + + } catch (Exception e) { + this.logWarn(this.log, e.getMessage()); + } + } + + this._setRelay(relayStatus); + this._setActivePower(activePower); + this._setCurrent(current); + this._setVoltage(voltage); + this.channel(IoShellyPlusPlugs.ChannelId.HAS_UPDATE).setNextValue(updatesAvailable); + } + + /** + * Execute on Cycle Event "Execute Write". + */ + private void executeWrite() { + var channel = this.getRelayChannel(); + var readValue = channel.value().get(); + var writeValue = channel.getNextWriteValueAndReset(); + if (writeValue.isEmpty()) { + return; + } + if (Objects.equals(readValue, writeValue.get())) { + return; + } + var index = 0; + final var url = this.baseUrl + "/relay/" + index + "?turn=" + (writeValue.get() ? "on" : "off"); + + this.httpBridge.get(url).whenComplete((t, e) -> { + this._setSlaveCommunicationFailed(e != null); + if (e == null) { + this.logDebug(this.log, "Executed write successfully for URL: " + url); + } else { + this.logError(this.log, "Failed to execute write for URL: " + url + "; Error: " + e.getMessage()); + } + }); + } + + /** + * Calculate the Energy values from ActivePower. + */ + private void calculateEnergy() { + // Calculate Energy + final var activePower = this.getActivePower().get(); + if (activePower == null) { + this.calculateProductionEnergy.update(null); + this.calculateConsumptionEnergy.update(null); + } else if (activePower >= 0) { + this.calculateProductionEnergy.update(activePower); + this.calculateConsumptionEnergy.update(0); + } else { + this.calculateProductionEnergy.update(0); + this.calculateConsumptionEnergy.update(-activePower); + } + } + + @Override + public MeterType getMeterType() { + return this.meterType; + } + + @Override + public SinglePhase getPhase() { + return this.phase; + } + + @Override + public Timedata getTimedata() { + return this.timedata; + } + +} diff --git a/io.openems.edge.io.shelly/test/io/openems/edge/io/shelly/shellyplusplugs/IoShellyPlugImplTest.java b/io.openems.edge.io.shelly/test/io/openems/edge/io/shelly/shellyplusplugs/IoShellyPlugImplTest.java new file mode 100644 index 00000000000..88da88fe9ff --- /dev/null +++ b/io.openems.edge.io.shelly/test/io/openems/edge/io/shelly/shellyplusplugs/IoShellyPlugImplTest.java @@ -0,0 +1,88 @@ +package io.openems.edge.io.shelly.shellyplusplugs; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import io.openems.common.types.ChannelAddress; +import io.openems.edge.bridge.http.dummy.DummyBridgeHttpFactory; +import io.openems.edge.common.test.AbstractComponentTest.TestCase; +import io.openems.edge.common.test.ComponentTest; +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; +import io.openems.edge.timedata.test.DummyTimedata; + +public class IoShellyPlugImplTest { + + private static final String COMPONENT_ID = "io0"; + + private static final ChannelAddress ACTIVE_POWER = new ChannelAddress(COMPONENT_ID, "ActivePower"); + private static final ChannelAddress ACTIVE_POWER_L1 = new ChannelAddress(COMPONENT_ID, "ActivePowerL1"); + private static final ChannelAddress ACTIVE_POWER_L2 = new ChannelAddress(COMPONENT_ID, "ActivePowerL2"); + private static final ChannelAddress CURRENT = new ChannelAddress(COMPONENT_ID, "Current"); + private static final ChannelAddress VOLTAGE = new ChannelAddress(COMPONENT_ID, "Voltage"); + private static final ChannelAddress PRODUCTION_ENERGY = new ChannelAddress(COMPONENT_ID, "ActiveProductionEnergy"); + private static final ChannelAddress CONSUMPTION_ENERGY = new ChannelAddress(COMPONENT_ID, + "ActiveConsumptionEnergy"); + + @Test + public void test() throws Exception { + final var bridgeFactory = new DummyBridgeHttpFactory(); + final var bridge = bridgeFactory.bridge; + final var sut = new IoShellyPlusPlugsImpl(); + new ComponentTest(sut) // + .addReference("httpBridgeFactory", bridgeFactory) // + .addReference("timedata", new DummyTimedata("timedata0")) // + .activate(MyConfig.create() // + .setId(COMPONENT_ID) // + .setPhase(SinglePhase.L1) // + .setIp("127.0.0.1") // + .setType(MeterType.PRODUCTION) // + .build()) // + + .next(new TestCase("Successfull read response") // + .onBeforeControllersCallbacks(() -> bridge.mockCycleResult(""" + { + "sys": { + "available_updates": { + "foo": "bar" + } + }, + "switch:0": { + "current": 1.234, + "voltage": 231.5, + "output": false, + "apower": 789.1 + } + } + """)) // + .output(ACTIVE_POWER, 789) // + .output(ACTIVE_POWER_L1, 789) // + .output(ACTIVE_POWER_L2, null) // + .output(CURRENT, 1234) // + .output(VOLTAGE, 231500)) // + + .next(new TestCase("Invalid read response") // + .onBeforeControllersCallbacks(() -> assertEquals("Off|789 W", sut.debugLog())) + + .onBeforeControllersCallbacks(() -> bridge.mockCycleResult("failed")) // + .output(ACTIVE_POWER, null) // + .output(ACTIVE_POWER_L1, null) // + .output(ACTIVE_POWER_L2, null) // + .output(CURRENT, null) // + .output(VOLTAGE, null) // + + .output(PRODUCTION_ENERGY, 0L) // + .output(CONSUMPTION_ENERGY, 0L)) // + + .next(new TestCase("Write") // + .onBeforeControllersCallbacks(() -> assertEquals("Unknown|UNDEFINED", sut.debugLog())) + + .onBeforeControllersCallbacks(() -> { + sut.setRelay(true); + bridge.mockRequestResult("FOO-BAR"); + })) // + + .deactivate(); + } +} diff --git a/io.openems.edge.io.shelly/test/io/openems/edge/io/shelly/shellyplusplugs/MyConfig.java b/io.openems.edge.io.shelly/test/io/openems/edge/io/shelly/shellyplusplugs/MyConfig.java new file mode 100644 index 00000000000..81fe32540f0 --- /dev/null +++ b/io.openems.edge.io.shelly/test/io/openems/edge/io/shelly/shellyplusplugs/MyConfig.java @@ -0,0 +1,75 @@ +package io.openems.edge.io.shelly.shellyplusplugs; + +import io.openems.common.test.AbstractComponentConfig; +import io.openems.edge.io.shelly.shellyplusplugs.Config; +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; + +@SuppressWarnings("all") +public class MyConfig extends AbstractComponentConfig implements Config { + + protected static class Builder { + private String id; + private String ip; + private MeterType type; + private SinglePhase phase; + + private Builder() { + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setPhase(SinglePhase phase) { + this.phase = phase; + return this; + } + + public Builder setIp(String ip) { + this.ip = ip; + return this; + } + + public Builder setType(MeterType type) { + this.type = type; + return this; + } + + public MyConfig build() { + return new MyConfig(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new Builder(); + } + + private final Builder builder; + + private MyConfig(Builder builder) { + super(Config.class, builder.id); + this.builder = builder; + } + + @Override + public SinglePhase phase() { + return this.builder.phase; + } + + @Override + public String ip() { + return this.builder.ip; + } + + @Override + public MeterType type() { + return this.builder.type; + } +} \ No newline at end of file diff --git a/io.openems.edge.io.wago/src/io/openems/edge/wago/IoWagoImpl.java b/io.openems.edge.io.wago/src/io/openems/edge/wago/IoWagoImpl.java index 250029d4998..d00fe280ee6 100644 --- a/io.openems.edge.io.wago/src/io/openems/edge/wago/IoWagoImpl.java +++ b/io.openems.edge.io.wago/src/io/openems/edge/wago/IoWagoImpl.java @@ -270,7 +270,7 @@ protected CoilElement createModbusCoilElement(io.openems.edge.common.channel.Cha } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { if (this.protocol == null) { this.protocol = new ModbusProtocol(this); } diff --git a/io.openems.edge.io.weidmueller/src/io/openems/edge/io/weidmueller/IoWeidmuellerUr20Impl.java b/io.openems.edge.io.weidmueller/src/io/openems/edge/io/weidmueller/IoWeidmuellerUr20Impl.java index 288bc173545..fdceb8c3564 100644 --- a/io.openems.edge.io.weidmueller/src/io/openems/edge/io/weidmueller/IoWeidmuellerUr20Impl.java +++ b/io.openems.edge.io.weidmueller/src/io/openems/edge/io/weidmueller/IoWeidmuellerUr20Impl.java @@ -1,5 +1,8 @@ package io.openems.edge.io.weidmueller; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementsOnce; + import java.util.ArrayList; import java.util.List; import java.util.TreeMap; @@ -29,6 +32,7 @@ import io.openems.edge.bridge.modbus.api.ModbusUtils; import io.openems.edge.bridge.modbus.api.element.BitsWordElement; import io.openems.edge.bridge.modbus.api.element.CoilElement; +import io.openems.edge.bridge.modbus.api.element.ModbusRegisterElement; import io.openems.edge.bridge.modbus.api.element.UnsignedDoublewordElement; import io.openems.edge.bridge.modbus.api.element.UnsignedWordElement; import io.openems.edge.bridge.modbus.api.task.FC3ReadRegistersTask; @@ -72,7 +76,7 @@ protected void setModbus(BridgeModbus modbus) { private BooleanReadChannel[] digitalInputChannels; private BooleanWriteChannel[] digitalOutputChannels; - public IoWeidmuellerUr20Impl() throws OpenemsException { + public IoWeidmuellerUr20Impl() { super(// OpenemsComponent.ChannelId.values(), // ModbusComponent.ChannelId.values(), // @@ -154,15 +158,7 @@ private void activate(ComponentContext context, Config config) throws OpenemsExc return; } - try { - this.modbusProtocol.addTasks(tasks); - - } catch (OpenemsException e) { - this.logError(this.log, "Unable to add Modbus-Task for U-Remote-Module #" + moduleCount // - + " [" + module.name() + "]: " + e.getMessage()); - e.printStackTrace(); - return; - } + this.modbusProtocol.addTasks(tasks); this.digitalInputChannels = this.modules.values().stream() // .flatMap(cs -> cs.stream()) // @@ -182,7 +178,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return this.modbusProtocol; } @@ -226,27 +222,16 @@ public String debugLog() { } private CompletableFuture readNumberOfEntriesInTheCurrentModuleList() { - try { - return ModbusUtils.readELementOnce(this.modbusProtocol, new UnsignedWordElement(0x27FE), true); - - } catch (OpenemsException e) { - this.logWarn(this.log, "Unable to readNumberOfEntriesInTheCurrentModuleList: " + e.getMessage()); - return CompletableFuture.failedFuture(e); - } + return readElementOnce(this.modbusProtocol, ModbusUtils::retryOnNull, new UnsignedWordElement(0x27FE)); } + @SuppressWarnings("unchecked") private CompletableFuture> readCurrentModuleList(int numberOfEntries) { var elements = IntStream.range(0, numberOfEntries) // .map(index -> 0x2A00 + index * 2) // .mapToObj(address -> new UnsignedDoublewordElement(address)) // - .toArray(UnsignedDoublewordElement[]::new); - try { - return ModbusUtils.readELementsOnce(this.modbusProtocol, elements, true); - - } catch (OpenemsException e) { - this.logWarn(this.log, "Unable to readCurrentModuleList: " + e.getMessage()); - return CompletableFuture.failedFuture(e); - } + .toArray(ModbusRegisterElement[]::new); + return readElementsOnce(this.modbusProtocol, ModbusUtils::retryOnNull, elements); } } diff --git a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhaseMeter.java b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhaseMeter.java index 07ccff0bc23..206766eaa16 100644 --- a/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhaseMeter.java +++ b/io.openems.edge.meter.api/src/io/openems/edge/meter/api/SinglePhaseMeter.java @@ -1,5 +1,9 @@ package io.openems.edge.meter.api; +import static io.openems.edge.meter.api.SinglePhase.L1; +import static io.openems.edge.meter.api.SinglePhase.L2; +import static io.openems.edge.meter.api.SinglePhase.L3; + import java.util.function.Function; import org.osgi.annotation.versioning.ProviderType; @@ -45,7 +49,7 @@ public Doc doc() { * @param meter the {@link SinglePhaseMeter} */ public static void calculateSinglePhaseFromActivePower(SinglePhaseMeter meter) { - SinglePhaseMeter.calculateSinglePhaseFromActivePower(meter, SinglePhaseMeter::getPhase); + calculateSinglePhaseFromActivePower(meter, SinglePhaseMeter::getPhase); } /** @@ -70,9 +74,136 @@ public static void calculateSinglePhaseFromActi Function phaseProvider) { meter.getActivePowerChannel().onSetNextValue(value -> { var phase = phaseProvider.apply(meter); - meter.getActivePowerL1Channel().setNextValue(phase == SinglePhase.L1 ? value : null); - meter.getActivePowerL2Channel().setNextValue(phase == SinglePhase.L2 ? value : null); - meter.getActivePowerL3Channel().setNextValue(phase == SinglePhase.L3 ? value : null); + meter.getActivePowerL1Channel().setNextValue(phase == L1 ? value : null); + meter.getActivePowerL2Channel().setNextValue(phase == L2 ? value : null); + meter.getActivePowerL3Channel().setNextValue(phase == L3 ? value : null); + }); + } + + /** + * Initializes Channel listeners for a {@link SinglePhaseMeter}. + * + *

    + * Sets the correct value for {@link ChannelId#REACTIVE_POWER_L1}, + * {@link ChannelId#REACTIVE_POWER_L2} or {@link ChannelId#REACTIVE_POWER_L3} + * from {@link ChannelId#REACTIVE_POWER} by evaluating the configured + * {@link SinglePhase} via {@link SinglePhaseMeter#getPhase()}. + * + * @param meter the {@link SinglePhaseMeter} + */ + public static void calculateSinglePhaseFromReactivePower(SinglePhaseMeter meter) { + calculateSinglePhaseFromReactivePower(meter, SinglePhaseMeter::getPhase); + } + + /** + * Initializes Channel listeners for a {@link SinglePhaseMeter}. + * + *

    + * Use this method if it is not known at compile time, that the + * {@link ElectricityMeter} is a {@link SinglePhaseMeter}, i.e. it is not + * implementing {@link SinglePhaseMeter}. + * + *

    + * Sets the correct value for {@link ChannelId#REACTIVE_POWER_L1}, + * {@link ChannelId#REACTIVE_POWER_L2} or {@link ChannelId#REACTIVE_POWER_L3} + * from {@link ChannelId#REACTIVE_POWER} by evaluating the provided + * {@link SinglePhase}. + * + * @param type that extends {@link ElectricityMeter} + * @param meter a {@link ElectricityMeter} + * @param phaseProvider a provider for {@link SinglePhase} + */ + public static void calculateSinglePhaseFromReactivePower(METER meter, + Function phaseProvider) { + meter.getReactivePowerChannel().onSetNextValue(value -> { + var phase = phaseProvider.apply(meter); + meter.getReactivePowerL1Channel().setNextValue(phase == L1 ? value : null); + meter.getReactivePowerL2Channel().setNextValue(phase == L2 ? value : null); + meter.getReactivePowerL3Channel().setNextValue(phase == L3 ? value : null); + }); + } + + /** + * Initializes Channel listeners for a {@link SinglePhaseMeter}. + * + *

    + * Sets the correct value for {@link ChannelId#CURRENT_L1}, + * {@link ChannelId#CURRENT_L2} or {@link ChannelId#CURRENT_L3} from + * {@link ChannelId#CURRENT} by evaluating the configured {@link SinglePhase} + * via {@link SinglePhaseMeter#getPhase()}. + * + * @param meter the {@link SinglePhaseMeter} + */ + public static void calculateSinglePhaseFromCurrent(SinglePhaseMeter meter) { + calculateSinglePhaseFromCurrent(meter, SinglePhaseMeter::getPhase); + } + + /** + * Initializes Channel listeners for a {@link SinglePhaseMeter}. + * + *

    + * Use this method if it is not known at compile time, that the + * {@link ElectricityMeter} is a {@link SinglePhaseMeter}, i.e. it is not + * implementing {@link SinglePhaseMeter}. + * + *

    + * Sets the correct value for {@link ChannelId#CURRENT_L1}, + * {@link ChannelId#CURRENT_L2} or {@link ChannelId#CURRENT_L3} from + * {@link ChannelId#CURRENT} by evaluating the provided {@link SinglePhase}. + * + * @param type that extends {@link ElectricityMeter} + * @param meter a {@link ElectricityMeter} + * @param phaseProvider a provider for {@link SinglePhase} + */ + public static void calculateSinglePhaseFromCurrent(METER meter, + Function phaseProvider) { + meter.getCurrentChannel().onSetNextValue(value -> { + var phase = phaseProvider.apply(meter); + meter.getCurrentL1Channel().setNextValue(phase == L1 ? value : null); + meter.getCurrentL2Channel().setNextValue(phase == L2 ? value : null); + meter.getCurrentL3Channel().setNextValue(phase == L3 ? value : null); + }); + } + + /** + * Initializes Channel listeners for a {@link SinglePhaseMeter}. + * + *

    + * Sets the correct value for {@link ChannelId#VOLTAGE_L1}, + * {@link ChannelId#VOLTAGE_L2} or {@link ChannelId#VOLTAGE_L3} from + * {@link ChannelId#VOLTAGE} by evaluating the configured {@link SinglePhase} + * via {@link SinglePhaseMeter#getPhase()}. + * + * @param meter the {@link SinglePhaseMeter} + */ + public static void calculateSinglePhaseFromVoltage(SinglePhaseMeter meter) { + calculateSinglePhaseFromVoltage(meter, SinglePhaseMeter::getPhase); + } + + /** + * Initializes Channel listeners for a {@link SinglePhaseMeter}. + * + *

    + * Use this method if it is not known at compile time, that the + * {@link ElectricityMeter} is a {@link SinglePhaseMeter}, i.e. it is not + * implementing {@link SinglePhaseMeter}. + * + *

    + * Sets the correct value for {@link ChannelId#VOLTAGE_L1}, + * {@link ChannelId#VOLTAGE_L2} or {@link ChannelId#VOLTAGE_L3} from + * {@link ChannelId#VOLTAGE} by evaluating the provided {@link SinglePhase}. + * + * @param type that extends {@link ElectricityMeter} + * @param meter a {@link ElectricityMeter} + * @param phaseProvider a provider for {@link SinglePhase} + */ + public static void calculateSinglePhaseFromVoltage(METER meter, + Function phaseProvider) { + meter.getVoltageChannel().onSetNextValue(value -> { + var phase = phaseProvider.apply(meter); + meter.getVoltageL1Channel().setNextValue(phase == L1 ? value : null); + meter.getVoltageL2Channel().setNextValue(phase == L2 ? value : null); + meter.getVoltageL3Channel().setNextValue(phase == L3 ? value : null); }); } diff --git a/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/MeterArtemesAM2Impl.java b/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/MeterArtemesAM2Impl.java index 4682151c3c1..52db9a3acd0 100644 --- a/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/MeterArtemesAM2Impl.java +++ b/io.openems.edge.meter.artemes.am2/src/io/openems/edge/meter/artemes/am2/MeterArtemesAM2Impl.java @@ -85,7 +85,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, new FC4ReadInputRegistersTask(0x0000, Priority.HIGH, m(ElectricityMeter.ChannelId.VOLTAGE_L1, new UnsignedDoublewordElement(0x0000)), diff --git a/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/MeterBControlEM300Impl.java b/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/MeterBControlEM300Impl.java index 44339f8512d..3cd11ad580e 100644 --- a/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/MeterBControlEM300Impl.java +++ b/io.openems.edge.meter.bcontrol.em300/src/io/openems/edge/meter/bcontrol/em300/MeterBControlEM300Impl.java @@ -92,7 +92,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var modbusProtocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(0, Priority.HIGH, // m(MeterBControlEM300.ChannelId.ACTIVE_POWER_POS, new UnsignedDoublewordElement(0), diff --git a/io.openems.edge.meter.bgetech/src/io/openems/edge/meter/bgetech/MeterBgeTechDrt428M2Impl.java b/io.openems.edge.meter.bgetech/src/io/openems/edge/meter/bgetech/MeterBgeTechDrt428M2Impl.java index fa0546c7c10..39b7909110c 100644 --- a/io.openems.edge.meter.bgetech/src/io/openems/edge/meter/bgetech/MeterBgeTechDrt428M2Impl.java +++ b/io.openems.edge.meter.bgetech/src/io/openems/edge/meter/bgetech/MeterBgeTechDrt428M2Impl.java @@ -85,7 +85,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { ModbusProtocol modbusProtocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(14, Priority.HIGH, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new FloatDoublewordElement(14), SCALE_FACTOR_3), // diff --git a/io.openems.edge.meter.camillebauer.aplus/src/io/openems/edge/meter/camillebauer/aplus/MeterCamillebauerAplusImpl.java b/io.openems.edge.meter.camillebauer.aplus/src/io/openems/edge/meter/camillebauer/aplus/MeterCamillebauerAplusImpl.java index e519ac0fd59..77bd4a98c39 100644 --- a/io.openems.edge.meter.camillebauer.aplus/src/io/openems/edge/meter/camillebauer/aplus/MeterCamillebauerAplusImpl.java +++ b/io.openems.edge.meter.camillebauer.aplus/src/io/openems/edge/meter/camillebauer/aplus/MeterCamillebauerAplusImpl.java @@ -110,7 +110,7 @@ protected void deactivate() { * in 99. */ @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(99, Priority.HIGH, // m(ElectricityMeter.ChannelId.VOLTAGE, // diff --git a/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/MeterCarloGavazziEm300Impl.java b/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/MeterCarloGavazziEm300Impl.java index da837045d71..d5aea0a4d23 100644 --- a/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/MeterCarloGavazziEm300Impl.java +++ b/io.openems.edge.meter.carlo.gavazzi.em300/src/io/openems/edge/meter/carlo/gavazzi/em300/MeterCarloGavazziEm300Impl.java @@ -85,7 +85,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var offset = 300000 + 1; /** * See Modbus definition PDF-file in doc directory and diff --git a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/MeterDiscovergy.java b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/MeterDiscovergy.java index f99a6062584..80874dbc5e7 100644 --- a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/MeterDiscovergy.java +++ b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/MeterDiscovergy.java @@ -6,10 +6,9 @@ import io.openems.common.types.OpenemsType; import io.openems.edge.common.channel.Doc; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; import io.openems.edge.meter.api.ElectricityMeter; -public interface MeterDiscovergy extends ElectricityMeter, OpenemsComponent, EventHandler, JsonApi { +public interface MeterDiscovergy extends ElectricityMeter, OpenemsComponent, EventHandler { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { /* diff --git a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/MeterDiscovergyImpl.java b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/MeterDiscovergyImpl.java index 007eb33ca9b..24884b6d2d6 100644 --- a/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/MeterDiscovergyImpl.java +++ b/io.openems.edge.meter.discovergy/src/io/openems/edge/meter/discovergy/MeterDiscovergyImpl.java @@ -2,7 +2,6 @@ import java.util.HashSet; import java.util.Set; -import java.util.concurrent.CompletableFuture; import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Activate; @@ -17,16 +16,14 @@ import com.google.gson.JsonElement; -import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; -import io.openems.common.session.Role; import io.openems.common.utils.JsonUtils; import io.openems.edge.common.component.AbstractOpenemsComponent; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.event.EdgeEventConstants; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApiBuilder; import io.openems.edge.common.user.User; import io.openems.edge.meter.api.ElectricityMeter; import io.openems.edge.meter.api.MeterType; @@ -46,7 +43,7 @@ EdgeEventConstants.TOPIC_CYCLE_BEFORE_PROCESS_IMAGE // }) public class MeterDiscovergyImpl extends AbstractOpenemsComponent - implements MeterDiscovergy, ElectricityMeter, OpenemsComponent, EventHandler, JsonApi { + implements MeterDiscovergy, ElectricityMeter, OpenemsComponent, EventHandler, ComponentJsonApi { private MeterType meterType = MeterType.PRODUCTION; private DiscovergyApiClient apiClient = null; @@ -118,21 +115,20 @@ protected void logError(Logger log, String message) { } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException { - user.assertRoleIsAtLeast("handleJsonrpcRequest", Role.GUEST); - - switch (request.getMethod()) { - - case GetMetersRequest.METHOD: - return this.handleGetMetersRequest(user, GetMetersRequest.from(request)); - - case GetFieldNamesRequest.METHOD: - return this.handleGetFieldNamesRequest(user, GetFieldNamesRequest.from(request)); - - default: - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(GetMetersRequest.METHOD, call -> { + return this.handleGetMetersRequest(// + call.get(EdgeKeys.USER_KEY), // + GetMetersRequest.from(call.getRequest()) // + ); + }); + + builder.handleRequest(GetFieldNamesRequest.METHOD, call -> { + return this.handleGetFieldNamesRequest(// + call.get(EdgeKeys.USER_KEY), // + GetFieldNamesRequest.from(call.getRequest()) // + ); + }); } /** @@ -143,14 +139,12 @@ public CompletableFuture handleJsonrpcRequest( * * @param user the User * @param request the GetMetersRequest - * @return the Future JSON-RPC Response + * @return the Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleGetMetersRequest(User user, GetMetersRequest request) - throws OpenemsNamedException { + private GetMetersResponse handleGetMetersRequest(User user, GetMetersRequest request) throws OpenemsNamedException { var meters = this.apiClient.getMeters(); - var response = new GetMetersResponse(request.getId(), meters); - return CompletableFuture.completedFuture(response); + return new GetMetersResponse(request.getId(), meters); } /** @@ -161,17 +155,16 @@ private CompletableFuture handleGetMetersRequest(User us * * @param user the User * @param request the GetFieldNamesRequest - * @return the Future JSON-RPC Response + * @return the Response * @throws OpenemsNamedException on error */ - private CompletableFuture handleGetFieldNamesRequest(User user, - GetFieldNamesRequest request) throws OpenemsNamedException { + private GetFieldNamesResponse handleGetFieldNamesRequest(User user, GetFieldNamesRequest request) + throws OpenemsNamedException { var fieldNames = this.apiClient.getFieldNames(request.getMeterId()); Set fields = new HashSet<>(); for (JsonElement fieldNameElement : fieldNames) { fields.add(JsonUtils.getAsEnum(Field.class, fieldNameElement)); } - var response = new GetFieldNamesResponse(request.getId(), fields); - return CompletableFuture.completedFuture(response); + return new GetFieldNamesResponse(request.getId(), fields); } } diff --git a/io.openems.edge.meter.eastron/.classpath b/io.openems.edge.meter.eastron/.classpath new file mode 100644 index 00000000000..bbfbdbe40e7 --- /dev/null +++ b/io.openems.edge.meter.eastron/.classpath @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/io.openems.edge.meter.microcare.sdm630/.gitignore b/io.openems.edge.meter.eastron/.gitignore similarity index 100% rename from io.openems.edge.meter.microcare.sdm630/.gitignore rename to io.openems.edge.meter.eastron/.gitignore diff --git a/io.openems.edge.meter.eastron/.project b/io.openems.edge.meter.eastron/.project new file mode 100644 index 00000000000..15128f1ed59 --- /dev/null +++ b/io.openems.edge.meter.eastron/.project @@ -0,0 +1,23 @@ + + + io.openems.edge.meter.eastron + + + + + + org.eclipse.jdt.core.javabuilder + + + + + bndtools.core.bndbuilder + + + + + + org.eclipse.jdt.core.javanature + bndtools.core.bndnature + + diff --git a/io.openems.edge.meter.eastron/.settings/org.eclipse.core.resources.prefs b/io.openems.edge.meter.eastron/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000000..99f26c0203a --- /dev/null +++ b/io.openems.edge.meter.eastron/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/io.openems.edge.meter.microcare.sdm630/bnd.bnd b/io.openems.edge.meter.eastron/bnd.bnd similarity index 84% rename from io.openems.edge.meter.microcare.sdm630/bnd.bnd rename to io.openems.edge.meter.eastron/bnd.bnd index 39fb3f2b3fc..ba1e48e8ad4 100644 --- a/io.openems.edge.meter.microcare.sdm630/bnd.bnd +++ b/io.openems.edge.meter.eastron/bnd.bnd @@ -1,4 +1,4 @@ -Bundle-Name: OpenEMS Edge Meter Microcare SDM 630 Smart Meter +Bundle-Name: OpenEMS Edge Meter Eastron SDM 630 Smart Meter Bundle-Vendor: Microcare (Destrier Electronics Pty Ltd) Bundle-Version: 1.0.0.${tstamp} Bundle-License: Proprietary (for now) diff --git a/io.openems.edge.meter.eastron/doc/SDM120-MODBUS_Protocol.pdf b/io.openems.edge.meter.eastron/doc/SDM120-MODBUS_Protocol.pdf new file mode 100644 index 00000000000..82524143368 Binary files /dev/null and b/io.openems.edge.meter.eastron/doc/SDM120-MODBUS_Protocol.pdf differ diff --git a/io.openems.edge.meter.microcare.sdm630/doc/SDM630Register1-5.pdf b/io.openems.edge.meter.eastron/doc/SDM630Register1-5.pdf similarity index 100% rename from io.openems.edge.meter.microcare.sdm630/doc/SDM630Register1-5.pdf rename to io.openems.edge.meter.eastron/doc/SDM630Register1-5.pdf diff --git a/io.openems.edge.meter.microcare.sdm630/readme.adoc b/io.openems.edge.meter.eastron/readme.adoc similarity index 55% rename from io.openems.edge.meter.microcare.sdm630/readme.adoc rename to io.openems.edge.meter.eastron/readme.adoc index f4e64498a14..d8ccfd5ef52 100644 --- a/io.openems.edge.meter.microcare.sdm630/readme.adoc +++ b/io.openems.edge.meter.eastron/readme.adoc @@ -1,8 +1,11 @@ -= Microcare SDM 630 Meter += Eastron/Microcare SDM 630 and 120 Meter -This implementation is functionally compatible with a number of energy meters with the name "SDM 630". +This implementation is functionally compatible with a number of energy meters with the names: + +- SDM 630 and +- SDM 120 Implemented Natures: - ElectricityMeter -https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.microcare.sdm630[Source Code icon:github[]] \ No newline at end of file +https://github.com/OpenEMS/openems/tree/develop/io.openems.edge.meter.eastron[Source Code icon:github[]] \ No newline at end of file diff --git a/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm120/Config.java b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm120/Config.java new file mode 100644 index 00000000000..1800860d9c5 --- /dev/null +++ b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm120/Config.java @@ -0,0 +1,41 @@ +package io.openems.edge.meter.eastron.sdm120; + +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; + +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; + +@ObjectClassDefinition(name = "Meter Eastron SDM 120", // + description = "Implements the Eastron SDM120 meter.") +@interface Config { + + @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") + String id() default "meter0"; + + @AttributeDefinition(name = "Alias", description = "Human-readable name of this Component; defaults to Component-ID") + String alias() default ""; + + @AttributeDefinition(name = "Is enabled?", description = "Is this Component enabled?") + boolean enabled() default true; + + @AttributeDefinition(name = "Meter-Type", description = "Grid (default), Production, Consumption") + MeterType type() default MeterType.GRID; + + @AttributeDefinition(name = "Modbus-ID", description = "ID of Modbus bridge.") + String modbus_id() default "modbus0"; + + @AttributeDefinition(name = "Modbus Unit-ID", description = "The Unit-ID of the Modbus device.") + int modbusUnitId() default 1; + + @AttributeDefinition(name = "Invert Power", description = "Inverts ALL Power values, inverts current values, swaps production and consumption energy, i.e. Power is multiplied with -1.") + boolean invert() default false; + + @AttributeDefinition(name = "Phase", description = "Which Phase is this Meter connected to?") + SinglePhase phase() default SinglePhase.L1; + + @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") + String Modbus_target() default "(enabled=true)"; + + String webconsole_configurationFactory_nameHint() default "Meter Eastron SDM 120 [{id}]"; +} \ No newline at end of file diff --git a/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm120/MeterEastronSdm120.java b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm120/MeterEastronSdm120.java new file mode 100644 index 00000000000..3fa5e9d2b44 --- /dev/null +++ b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm120/MeterEastronSdm120.java @@ -0,0 +1,32 @@ +package io.openems.edge.meter.eastron.sdm120; + +import io.openems.common.channel.Unit; +import io.openems.common.types.OpenemsType; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.meter.api.ElectricityMeter; +import io.openems.edge.meter.api.SinglePhaseMeter; + +public interface MeterEastronSdm120 extends SinglePhaseMeter, ElectricityMeter, OpenemsComponent, ModbusSlave { + + public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + REACTIVE_PRODUCTION_ENERGY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT_AMPERE_REACTIVE_HOURS)), // + REACTIVE_CONSUMPTION_ENERGY(Doc.of(OpenemsType.INTEGER) // + .unit(Unit.VOLT_AMPERE_REACTIVE_HOURS)), // + ; + + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + @Override + public Doc doc() { + return this.doc; + } + } + +} diff --git a/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm120/MeterEastronSdm120Impl.java b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm120/MeterEastronSdm120Impl.java new file mode 100644 index 00000000000..3667375bb8b --- /dev/null +++ b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm120/MeterEastronSdm120Impl.java @@ -0,0 +1,185 @@ +package io.openems.edge.meter.eastron.sdm120; + +import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.INVERT_IF_TRUE; +import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_3; +import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.chain; + +import org.osgi.service.cm.ConfigurationAdmin; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.ConfigurationPolicy; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.service.component.annotations.ReferencePolicyOption; +import org.osgi.service.event.Event; +import org.osgi.service.event.EventHandler; +import org.osgi.service.event.propertytypes.EventTopics; +import org.osgi.service.metatype.annotations.Designate; + +import io.openems.common.channel.AccessMode; +import io.openems.common.exceptions.OpenemsException; +import io.openems.edge.bridge.modbus.api.AbstractOpenemsModbusComponent; +import io.openems.edge.bridge.modbus.api.BridgeModbus; +import io.openems.edge.bridge.modbus.api.ModbusComponent; +import io.openems.edge.bridge.modbus.api.ModbusProtocol; +import io.openems.edge.bridge.modbus.api.element.DummyRegisterElement; +import io.openems.edge.bridge.modbus.api.element.FloatDoublewordElement; +import io.openems.edge.bridge.modbus.api.task.FC4ReadInputRegistersTask; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.event.EdgeEventConstants; +import io.openems.edge.common.modbusslave.ModbusSlave; +import io.openems.edge.common.modbusslave.ModbusSlaveTable; +import io.openems.edge.common.taskmanager.Priority; +import io.openems.edge.meter.api.ElectricityMeter; +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; +import io.openems.edge.meter.api.SinglePhaseMeter; +import io.openems.edge.timedata.api.Timedata; +import io.openems.edge.timedata.api.TimedataProvider; +import io.openems.edge.timedata.api.utils.CalculateEnergyFromPower; + +@Designate(ocd = Config.class, factory = true) +@Component(// + name = "Meter.Eastron.SDM120", // + immediate = true, // + configurationPolicy = ConfigurationPolicy.REQUIRE // +) +@EventTopics({ // + EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // +}) +public class MeterEastronSdm120Impl extends AbstractOpenemsModbusComponent + implements MeterEastronSdm120, SinglePhaseMeter, ElectricityMeter, ModbusComponent, OpenemsComponent, + ModbusSlave, TimedataProvider, EventHandler { + + private final CalculateEnergyFromPower calculateProductionEnergy = new CalculateEnergyFromPower(this, + ElectricityMeter.ChannelId.ACTIVE_PRODUCTION_ENERGY); + private final CalculateEnergyFromPower calculateConsumptionEnergy = new CalculateEnergyFromPower(this, + ElectricityMeter.ChannelId.ACTIVE_CONSUMPTION_ENERGY); + + @Reference + private ConfigurationAdmin cm; + + @Reference(policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.OPTIONAL) + private volatile Timedata timedata; + + @Override + @Reference(policy = ReferencePolicy.STATIC, policyOption = ReferencePolicyOption.GREEDY, cardinality = ReferenceCardinality.MANDATORY) + protected void setModbus(BridgeModbus modbus) { + super.setModbus(modbus); + } + + private Config config; + + public MeterEastronSdm120Impl() { + super(// + OpenemsComponent.ChannelId.values(), // + ModbusComponent.ChannelId.values(), // + ElectricityMeter.ChannelId.values(), // + SinglePhaseMeter.ChannelId.values(), // + MeterEastronSdm120.ChannelId.values() // + ); + + SinglePhaseMeter.calculateSinglePhaseFromActivePower(this); + SinglePhaseMeter.calculateSinglePhaseFromReactivePower(this); + SinglePhaseMeter.calculateSinglePhaseFromCurrent(this); + SinglePhaseMeter.calculateSinglePhaseFromVoltage(this); + } + + @Activate + private void activate(ComponentContext context, Config config) throws OpenemsException { + this.config = config; + if (super.activate(context, config.id(), config.alias(), config.enabled(), config.modbusUnitId(), this.cm, + "Modbus", config.modbus_id())) { + return; + } + } + + @Override + @Deactivate + protected void deactivate() { + super.deactivate(); + } + + @Override + public MeterType getMeterType() { + return this.config.type(); + } + + @Override + protected ModbusProtocol defineModbusProtocol() { + final var offset = 30001; + final var invert = this.config.invert(); + + return new ModbusProtocol(this, // + new FC4ReadInputRegistersTask(30001 - offset, Priority.HIGH, // + m(ElectricityMeter.ChannelId.VOLTAGE, new FloatDoublewordElement(30001 - offset), + SCALE_FACTOR_3), // + new DummyRegisterElement(30003 - offset, 30006 - offset), // + m(ElectricityMeter.ChannelId.CURRENT, new FloatDoublewordElement(30007 - offset), + chain(SCALE_FACTOR_3, INVERT_IF_TRUE(invert))), // + new DummyRegisterElement(30009 - offset, 30012 - offset), // + m(ElectricityMeter.ChannelId.ACTIVE_POWER, new FloatDoublewordElement(30013 - offset), + INVERT_IF_TRUE(invert)), // + new DummyRegisterElement(30015 - offset, 30024 - offset), // + m(ElectricityMeter.ChannelId.REACTIVE_POWER, new FloatDoublewordElement(30025 - offset), + INVERT_IF_TRUE(invert)), // + new DummyRegisterElement(30027 - offset, 30070 - offset), // + m(ElectricityMeter.ChannelId.FREQUENCY, new FloatDoublewordElement(30071 - offset), + SCALE_FACTOR_3))); + } + + @Override + public String debugLog() { + return "L:" + this.getActivePower().asString(); + } + + @Override + public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { + return new ModbusSlaveTable(// + OpenemsComponent.getModbusSlaveNatureTable(accessMode), // + ElectricityMeter.getModbusSlaveNatureTable(accessMode), // + SinglePhaseMeter.getModbusSlaveNatureTable(accessMode) // + ); + } + + @Override + public Timedata getTimedata() { + return this.timedata; + } + + @Override + public void handleEvent(Event event) { + if (!this.isEnabled()) { + return; + } + switch (event.getTopic()) { + case EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE -> this.calculateEnergy(); + } + } + + /** + * Calculate the Energy values from ActivePower. + */ + private void calculateEnergy() { + // Calculate Energy + final var activePower = this.getActivePower().get(); + if (activePower == null) { + this.calculateProductionEnergy.update(null); + this.calculateConsumptionEnergy.update(null); + } else if (activePower >= 0) { + this.calculateProductionEnergy.update(activePower); + this.calculateConsumptionEnergy.update(0); + } else { + this.calculateProductionEnergy.update(0); + this.calculateConsumptionEnergy.update(-activePower); + } + } + + @Override + public SinglePhase getPhase() { + return this.config.phase(); + } +} diff --git a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/Config.java b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm630/Config.java similarity index 86% rename from io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/Config.java rename to io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm630/Config.java index 2d663d771b8..072294858f1 100644 --- a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/Config.java +++ b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm630/Config.java @@ -1,12 +1,12 @@ -package io.openems.edge.meter.microcare.sdm630; +package io.openems.edge.meter.eastron.sdm630; import org.osgi.service.metatype.annotations.AttributeDefinition; import org.osgi.service.metatype.annotations.ObjectClassDefinition; import io.openems.edge.meter.api.MeterType; -@ObjectClassDefinition(name = "Meter Microcare SDM 630", // - description = "Implements the Microcare SDM630 meter.") +@ObjectClassDefinition(name = "Meter Eastron SDM 630", // + description = "Implements the Eastron SDM630 meter.") @interface Config { @AttributeDefinition(name = "Component-ID", description = "Unique ID of this Component") @@ -30,5 +30,5 @@ @AttributeDefinition(name = "Modbus target filter", description = "This is auto-generated by 'Modbus-ID'.") String Modbus_target() default "(enabled=true)"; - String webconsole_configurationFactory_nameHint() default "Meter Microcare SDM 630 [{id}]"; + String webconsole_configurationFactory_nameHint() default "Meter Eastron SDM 630 [{id}]"; } \ No newline at end of file diff --git a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSdm630.java b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm630/MeterEastronSdm630.java similarity index 83% rename from io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSdm630.java rename to io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm630/MeterEastronSdm630.java index 269595affb5..344f1703832 100644 --- a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSdm630.java +++ b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm630/MeterEastronSdm630.java @@ -1,4 +1,4 @@ -package io.openems.edge.meter.microcare.sdm630; +package io.openems.edge.meter.eastron.sdm630; import io.openems.common.channel.Unit; import io.openems.common.types.OpenemsType; @@ -7,7 +7,7 @@ import io.openems.edge.common.modbusslave.ModbusSlave; import io.openems.edge.meter.api.ElectricityMeter; -public interface MeterMicrocareSdm630 extends ElectricityMeter, OpenemsComponent, ModbusSlave { +public interface MeterEastronSdm630 extends ElectricityMeter, OpenemsComponent, ModbusSlave { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { REACTIVE_PRODUCTION_ENERGY(Doc.of(OpenemsType.INTEGER) // diff --git a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSdm630Impl.java b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm630/MeterEastronSdm630Impl.java similarity index 94% rename from io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSdm630Impl.java rename to io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm630/MeterEastronSdm630Impl.java index 8ab87bab921..b636a7f42da 100644 --- a/io.openems.edge.meter.microcare.sdm630/src/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSdm630Impl.java +++ b/io.openems.edge.meter.eastron/src/io/openems/edge/meter/eastron/sdm630/MeterEastronSdm630Impl.java @@ -1,4 +1,4 @@ -package io.openems.edge.meter.microcare.sdm630; +package io.openems.edge.meter.eastron.sdm630; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.DIRECT_1_TO_1; import static io.openems.edge.bridge.modbus.api.ElementToChannelConverter.SCALE_FACTOR_3; @@ -41,6 +41,7 @@ import io.openems.edge.timedata.api.TimedataProvider; import io.openems.edge.timedata.api.utils.CalculateEnergyFromPower; +// NOTE: we stick with the name `Meter.Microcare.SDM630` for backwards compatibility @Designate(ocd = Config.class, factory = true) @Component(// name = "Meter.Microcare.SDM630", // @@ -50,7 +51,7 @@ @EventTopics({ // EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE // }) -public class MeterMicrocareSdm630Impl extends AbstractOpenemsModbusComponent implements MeterMicrocareSdm630, +public class MeterEastronSdm630Impl extends AbstractOpenemsModbusComponent implements MeterEastronSdm630, ElectricityMeter, ModbusComponent, OpenemsComponent, ModbusSlave, TimedataProvider, EventHandler { private final CalculateEnergyFromPower calculateProductionEnergy = new CalculateEnergyFromPower(this, @@ -72,12 +73,12 @@ protected void setModbus(BridgeModbus modbus) { super.setModbus(modbus); } - public MeterMicrocareSdm630Impl() { + public MeterEastronSdm630Impl() { super(// OpenemsComponent.ChannelId.values(), // ModbusComponent.ChannelId.values(), // ElectricityMeter.ChannelId.values(), // - MeterMicrocareSdm630.ChannelId.values() // + MeterEastronSdm630.ChannelId.values() // ); // Automatically calculate sum values from L1/L2/L3 @@ -105,7 +106,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var offset = 30001; return new ModbusProtocol(this, // new FC4ReadInputRegistersTask(30001 - offset, Priority.HIGH, @@ -179,11 +180,11 @@ protected ModbusProtocol defineModbusProtocol() throws OpenemsException { .byteOrder(ByteOrder.BIG_ENDIAN), DIRECT_1_TO_1), new DummyRegisterElement(30073 - offset, 30076 - offset), // - m(MeterMicrocareSdm630.ChannelId.REACTIVE_PRODUCTION_ENERGY, + m(MeterEastronSdm630.ChannelId.REACTIVE_PRODUCTION_ENERGY, new FloatDoublewordElement(30077 - offset).wordOrder(WordOrder.MSWLSW) .byteOrder(ByteOrder.BIG_ENDIAN), SCALE_FACTOR_3), - m(MeterMicrocareSdm630.ChannelId.REACTIVE_CONSUMPTION_ENERGY, + m(MeterEastronSdm630.ChannelId.REACTIVE_CONSUMPTION_ENERGY, new FloatDoublewordElement(30079 - offset).wordOrder(WordOrder.MSWLSW) .byteOrder(ByteOrder.BIG_ENDIAN), SCALE_FACTOR_3))); diff --git a/io.openems.edge.meter.eastron/test/.gitignore b/io.openems.edge.meter.eastron/test/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm120/MeterEastronSdm120ImplTest.java b/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm120/MeterEastronSdm120ImplTest.java new file mode 100644 index 00000000000..169778c8b20 --- /dev/null +++ b/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm120/MeterEastronSdm120ImplTest.java @@ -0,0 +1,29 @@ +package io.openems.edge.meter.eastron.sdm120; + +import org.junit.Test; + +import io.openems.edge.bridge.modbus.test.DummyModbusBridge; +import io.openems.edge.common.test.ComponentTest; +import io.openems.edge.common.test.DummyConfigurationAdmin; +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; + +public class MeterEastronSdm120ImplTest { + + private static final String METER_ID = "meter0"; + private static final String MODBUS_ID = "modbus0"; + + @Test + public void test() throws Exception { + new ComponentTest(new MeterEastronSdm120Impl()) // + .addReference("cm", new DummyConfigurationAdmin()) // + .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // + .activate(MyConfig.create() // + .setId(METER_ID) // + .setModbusId(MODBUS_ID) // + .setType(MeterType.GRID) // + .setPhase(SinglePhase.L1) // + .build()) // + ; + } +} \ No newline at end of file diff --git a/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm120/MyConfig.java b/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm120/MyConfig.java new file mode 100644 index 00000000000..f1e83072c87 --- /dev/null +++ b/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm120/MyConfig.java @@ -0,0 +1,98 @@ +package io.openems.edge.meter.eastron.sdm120; + +import io.openems.common.test.AbstractComponentConfig; +import io.openems.common.utils.ConfigUtils; +import io.openems.edge.meter.api.MeterType; +import io.openems.edge.meter.api.SinglePhase; + +@SuppressWarnings("all") +public class MyConfig extends AbstractComponentConfig implements Config { + + protected static class Builder { + private String id; + private String modbusId; + private int modbusUnitId; + private MeterType type; + private SinglePhase phase; + private boolean invert; + + private Builder() { + } + + public Builder setId(String id) { + this.id = id; + return this; + } + + public Builder setModbusId(String modbusId) { + this.modbusId = modbusId; + return this; + } + + public Builder setType(MeterType type) { + this.type = type; + return this; + } + + public Builder setPhase(SinglePhase phase) { + this.phase = phase; + return this; + } + + public Builder setInvert(boolean invert) { + this.invert = invert; + return this; + } + + public MyConfig build() { + return new MyConfig(this); + } + } + + /** + * Create a Config builder. + * + * @return a {@link Builder} + */ + public static Builder create() { + return new Builder(); + } + + private final Builder builder; + + private MyConfig(Builder builder) { + super(Config.class, builder.id); + this.builder = builder; + } + + @Override + public String modbus_id() { + return this.builder.modbusId; + } + + @Override + public String Modbus_target() { + return ConfigUtils.generateReferenceTargetFilter(this.id(), this.modbus_id()); + } + + @Override + public int modbusUnitId() { + return this.builder.modbusUnitId; + } + + @Override + public MeterType type() { + return this.builder.type; + } + + @Override + public boolean invert() { + return this.builder.invert; + } + + @Override + public SinglePhase phase() { + return this.builder.phase; + } + +} \ No newline at end of file diff --git a/io.openems.edge.meter.microcare.sdm630/test/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSdm630ImplTest.java b/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm630/MeterEastronSdm630ImplTest.java similarity index 82% rename from io.openems.edge.meter.microcare.sdm630/test/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSdm630ImplTest.java rename to io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm630/MeterEastronSdm630ImplTest.java index 64b02b4862b..d79ef87a631 100644 --- a/io.openems.edge.meter.microcare.sdm630/test/io/openems/edge/meter/microcare/sdm630/MeterMicrocareSdm630ImplTest.java +++ b/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm630/MeterEastronSdm630ImplTest.java @@ -1,4 +1,4 @@ -package io.openems.edge.meter.microcare.sdm630; +package io.openems.edge.meter.eastron.sdm630; import org.junit.Test; @@ -7,14 +7,14 @@ import io.openems.edge.common.test.DummyConfigurationAdmin; import io.openems.edge.meter.api.MeterType; -public class MeterMicrocareSdm630ImplTest { +public class MeterEastronSdm630ImplTest { private static final String METER_ID = "meter0"; private static final String MODBUS_ID = "modbus0"; @Test public void test() throws Exception { - new ComponentTest(new MeterMicrocareSdm630Impl()) // + new ComponentTest(new MeterEastronSdm630Impl()) // .addReference("cm", new DummyConfigurationAdmin()) // .addReference("setModbus", new DummyModbusBridge(MODBUS_ID)) // .activate(MyConfig.create() // diff --git a/io.openems.edge.meter.microcare.sdm630/test/io/openems/edge/meter/microcare/sdm630/MyConfig.java b/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm630/MyConfig.java similarity index 96% rename from io.openems.edge.meter.microcare.sdm630/test/io/openems/edge/meter/microcare/sdm630/MyConfig.java rename to io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm630/MyConfig.java index 918f6b2339b..d686ca017c8 100644 --- a/io.openems.edge.meter.microcare.sdm630/test/io/openems/edge/meter/microcare/sdm630/MyConfig.java +++ b/io.openems.edge.meter.eastron/test/io/openems/edge/meter/eastron/sdm630/MyConfig.java @@ -1,4 +1,4 @@ -package io.openems.edge.meter.microcare.sdm630; +package io.openems.edge.meter.eastron.sdm630; import io.openems.common.test.AbstractComponentConfig; import io.openems.common.utils.ConfigUtils; diff --git a/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg511/MeterJanitzaUmg511Impl.java b/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg511/MeterJanitzaUmg511Impl.java index 5b9b915ba6c..b4ad5712068 100644 --- a/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg511/MeterJanitzaUmg511Impl.java +++ b/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg511/MeterJanitzaUmg511Impl.java @@ -95,7 +95,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var modbusProtocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(3845, Priority.HIGH, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new FloatDoublewordElement(3845), SCALE_FACTOR_3), // diff --git a/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg604/MeterJanitzaUmg604Impl.java b/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg604/MeterJanitzaUmg604Impl.java index 69f35ae04ec..95fa44bd68f 100644 --- a/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg604/MeterJanitzaUmg604Impl.java +++ b/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg604/MeterJanitzaUmg604Impl.java @@ -95,7 +95,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var modbusProtocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(1317, Priority.HIGH, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new FloatDoublewordElement(1317), // diff --git a/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg96rme/MeterJanitzaUmg96rmeImpl.java b/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg96rme/MeterJanitzaUmg96rmeImpl.java index b2f916b4ce3..7b9f91291a4 100644 --- a/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg96rme/MeterJanitzaUmg96rmeImpl.java +++ b/io.openems.edge.meter.janitza/src/io/openems/edge/meter/janitza/umg96rme/MeterJanitzaUmg96rmeImpl.java @@ -94,7 +94,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { /* * We are using the FLOAT registers from the modbus table, because they are all * reachable within one ReadMultipleRegistersRequest. diff --git a/io.openems.edge.meter.kdk/src/io/openems/edge/meter/kdk/puct2/MeterKdk2puctImpl.java b/io.openems.edge.meter.kdk/src/io/openems/edge/meter/kdk/puct2/MeterKdk2puctImpl.java index 2a4e65abc20..a8e380226d3 100755 --- a/io.openems.edge.meter.kdk/src/io/openems/edge/meter/kdk/puct2/MeterKdk2puctImpl.java +++ b/io.openems.edge.meter.kdk/src/io/openems/edge/meter/kdk/puct2/MeterKdk2puctImpl.java @@ -100,7 +100,7 @@ public ModbusSlaveTable getModbusSlaveTable(AccessMode accessMode) { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var modbusProtocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(0x4007, Priority.LOW, // m(MeterKdk2puct.ChannelId.SOFTWARE_VERSION, new FloatDoublewordElement(0x4007))), diff --git a/io.openems.edge.meter.phoenixcontact/src/io/openems/edge/meter/phoenixcontact/PhoenixContactMeterImpl.java b/io.openems.edge.meter.phoenixcontact/src/io/openems/edge/meter/phoenixcontact/PhoenixContactMeterImpl.java index 5645a4f13a1..146005b63ff 100644 --- a/io.openems.edge.meter.phoenixcontact/src/io/openems/edge/meter/phoenixcontact/PhoenixContactMeterImpl.java +++ b/io.openems.edge.meter.phoenixcontact/src/io/openems/edge/meter/phoenixcontact/PhoenixContactMeterImpl.java @@ -78,7 +78,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var modbusProtocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(0x8006, Priority.HIGH, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new FloatDoublewordElement(0x8006) // diff --git a/io.openems.edge.meter.plexlog/src/io/openems/edge/meter/plexlog/MeterPlexlogDataloggerImpl.java b/io.openems.edge.meter.plexlog/src/io/openems/edge/meter/plexlog/MeterPlexlogDataloggerImpl.java index c317670fd32..50bf5fa08f8 100644 --- a/io.openems.edge.meter.plexlog/src/io/openems/edge/meter/plexlog/MeterPlexlogDataloggerImpl.java +++ b/io.openems.edge.meter.plexlog/src/io/openems/edge/meter/plexlog/MeterPlexlogDataloggerImpl.java @@ -75,7 +75,7 @@ public String debugLog() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var modbusProtocol = new ModbusProtocol(this, // new FC4ReadInputRegistersTask(0, Priority.HIGH, // this.m(ElectricityMeter.ChannelId.ACTIVE_POWER, new SignedDoublewordElement(0)), // diff --git a/io.openems.edge.meter.pqplus/src/io/openems/edge/meter/pqplus/umd96/MeterPqplusUmd96Impl.java b/io.openems.edge.meter.pqplus/src/io/openems/edge/meter/pqplus/umd96/MeterPqplusUmd96Impl.java index a8064457fcb..248c2c30784 100644 --- a/io.openems.edge.meter.pqplus/src/io/openems/edge/meter/pqplus/umd96/MeterPqplusUmd96Impl.java +++ b/io.openems.edge.meter.pqplus/src/io/openems/edge/meter/pqplus/umd96/MeterPqplusUmd96Impl.java @@ -90,7 +90,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // // Frequency new FC3ReadRegistersTask(0x1004, Priority.LOW, // diff --git a/io.openems.edge.meter.pqplus/src/io/openems/edge/meter/pqplus/umd97/MeterPqplusUmd97Impl.java b/io.openems.edge.meter.pqplus/src/io/openems/edge/meter/pqplus/umd97/MeterPqplusUmd97Impl.java index ddda6519f0b..72accb117fb 100644 --- a/io.openems.edge.meter.pqplus/src/io/openems/edge/meter/pqplus/umd97/MeterPqplusUmd97Impl.java +++ b/io.openems.edge.meter.pqplus/src/io/openems/edge/meter/pqplus/umd97/MeterPqplusUmd97Impl.java @@ -87,7 +87,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(19000, Priority.HIGH, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new FloatDoublewordElement(19000), SCALE_FACTOR_3), diff --git a/io.openems.edge.meter.schneider.acti9.smartlink/src/io/openems/edge/meter/schneider/acti9/smartlink/MeterSchneiderActi9SmartlinkImpl.java b/io.openems.edge.meter.schneider.acti9.smartlink/src/io/openems/edge/meter/schneider/acti9/smartlink/MeterSchneiderActi9SmartlinkImpl.java index 23d3b54cbb2..f2d9c28e77c 100644 --- a/io.openems.edge.meter.schneider.acti9.smartlink/src/io/openems/edge/meter/schneider/acti9/smartlink/MeterSchneiderActi9SmartlinkImpl.java +++ b/io.openems.edge.meter.schneider.acti9.smartlink/src/io/openems/edge/meter/schneider/acti9/smartlink/MeterSchneiderActi9SmartlinkImpl.java @@ -85,7 +85,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var offset = 1; /** * See Datasheet PDF-file in doc directory. diff --git a/io.openems.edge.meter.siemens/src/io/openems/edge/meter/siemens/pac1600/MeterSiemensPac1600Impl.java b/io.openems.edge.meter.siemens/src/io/openems/edge/meter/siemens/pac1600/MeterSiemensPac1600Impl.java index 610f120c2eb..dbe1c463ce1 100644 --- a/io.openems.edge.meter.siemens/src/io/openems/edge/meter/siemens/pac1600/MeterSiemensPac1600Impl.java +++ b/io.openems.edge.meter.siemens/src/io/openems/edge/meter/siemens/pac1600/MeterSiemensPac1600Impl.java @@ -81,7 +81,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(1, Priority.HIGH, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new UnsignedDoublewordElement(1), diff --git a/io.openems.edge.meter.siemens/src/io/openems/edge/meter/siemens/pac2200/MeterSiemensPac2200Impl.java b/io.openems.edge.meter.siemens/src/io/openems/edge/meter/siemens/pac2200/MeterSiemensPac2200Impl.java index 4a71484d27a..3c2e8f5d7fa 100644 --- a/io.openems.edge.meter.siemens/src/io/openems/edge/meter/siemens/pac2200/MeterSiemensPac2200Impl.java +++ b/io.openems.edge.meter.siemens/src/io/openems/edge/meter/siemens/pac2200/MeterSiemensPac2200Impl.java @@ -95,8 +95,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { - + protected ModbusProtocol defineModbusProtocol() { var modbusProtocol = new ModbusProtocol(this, // new FC3ReadRegistersTask(1, Priority.HIGH, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new FloatDoublewordElement(1), SCALE_FACTOR_3), diff --git a/io.openems.edge.meter.sma.shm20/src/io/openems/edge/meter/sma/shm20/MeterSmaShm20Impl.java b/io.openems.edge.meter.sma.shm20/src/io/openems/edge/meter/sma/shm20/MeterSmaShm20Impl.java index 4aaf78ae43f..7e31503d6e9 100644 --- a/io.openems.edge.meter.sma.shm20/src/io/openems/edge/meter/sma/shm20/MeterSmaShm20Impl.java +++ b/io.openems.edge.meter.sma.shm20/src/io/openems/edge/meter/sma/shm20/MeterSmaShm20Impl.java @@ -85,7 +85,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { var modbusProtocol = new ModbusProtocol(this, // Consumption and Production Energy new FC3ReadRegistersTask(30581, Priority.HIGH, // diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/AbstractSocomecMeter.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/AbstractSocomecMeter.java index b3f9643e4c9..eaa80b7399f 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/AbstractSocomecMeter.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/AbstractSocomecMeter.java @@ -1,5 +1,7 @@ package io.openems.edge.meter.socomec; +import static io.openems.edge.bridge.modbus.api.ModbusUtils.readElementOnce; + import java.util.concurrent.CompletableFuture; import org.slf4j.Logger; @@ -23,7 +25,7 @@ public abstract class AbstractSocomecMeter extends AbstractOpenemsModbusComponen protected final ModbusProtocol modbusProtocol; protected AbstractSocomecMeter(io.openems.edge.common.channel.ChannelId[] firstInitialChannelIds, - io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) throws OpenemsException { + io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) { super(firstInitialChannelIds, furtherInitialChannelIds); this.modbusProtocol = new ModbusProtocol(this); } @@ -145,32 +147,18 @@ private CompletableFuture getSocomecIdentifier() { final var result = new CompletableFuture(); // Search for Socomec identifier register. Needs to be "SOCO". - try { - ModbusUtils.readELementOnce(this.modbusProtocol, new UnsignedQuadruplewordElement(0xC350), true) - .thenAccept(value -> { - if (value != 0x0053004F0043004FL /* SOCO */) { - this.channel(SocomecMeter.ChannelId.NO_SOCOMEC_METER).setNextValue(true); - return; - } - // Found Socomec meter - try { - ModbusUtils.readELementOnce(this.modbusProtocol, new StringWordElement(0xC38A, 8), true) - .thenAccept(name -> { - result.complete(name.toLowerCase()); - }); - - } catch (OpenemsException e) { - this.logWarn(this.log, "Error while trying to identify Socomec meter: " + e.getMessage()); - e.printStackTrace(); - result.complete(""); - } - }); - - } catch (OpenemsException e) { - this.logWarn(this.log, "Error while trying to identify Socomec meter: " + e.getMessage()); - e.printStackTrace(); - result.complete(""); - } + readElementOnce(this.modbusProtocol, ModbusUtils::retryOnNull, new UnsignedQuadruplewordElement(0xC350)) + .thenAccept(value -> { + if (value != 0x0053004F0043004FL /* SOCO */) { + this.channel(SocomecMeter.ChannelId.NO_SOCOMEC_METER).setNextValue(true); + // Complete result with Long value + result.complete(String.valueOf(value)); + } + readElementOnce(this.modbusProtocol, ModbusUtils::retryOnNull, new StringWordElement(0xC38A, 8)) + .thenAccept(name -> { + result.complete(name.toLowerCase()); + }); + }); return result; } diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/singlephase/MeterSocomecSinglephaseImpl.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/singlephase/MeterSocomecSinglephaseImpl.java index 36138cea305..04317f76ce8 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/singlephase/MeterSocomecSinglephaseImpl.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/singlephase/MeterSocomecSinglephaseImpl.java @@ -60,7 +60,7 @@ protected void setModbus(BridgeModbus modbus) { private Config config; - public MeterSocomecSinglephaseImpl() throws OpenemsException { + public MeterSocomecSinglephaseImpl() { super(// OpenemsComponent.ChannelId.values(), // ModbusComponent.ChannelId.values(), // diff --git a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/threephase/MeterSocomecThreephaseImpl.java b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/threephase/MeterSocomecThreephaseImpl.java index 2116d69b3bb..5152fbde0a0 100644 --- a/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/threephase/MeterSocomecThreephaseImpl.java +++ b/io.openems.edge.meter.socomec/src/io/openems/edge/meter/socomec/threephase/MeterSocomecThreephaseImpl.java @@ -58,7 +58,7 @@ protected void setModbus(BridgeModbus modbus) { private Config config; - public MeterSocomecThreephaseImpl() throws OpenemsException { + public MeterSocomecThreephaseImpl() { super(// OpenemsComponent.ChannelId.values(), // ModbusComponent.ChannelId.values(), // diff --git a/io.openems.edge.meter.sunspec/src/io/openems/edge/meter/sunspec/AbstractSunSpecMeter.java b/io.openems.edge.meter.sunspec/src/io/openems/edge/meter/sunspec/AbstractSunSpecMeter.java index f188d986c06..e5c8c0e8365 100644 --- a/io.openems.edge.meter.sunspec/src/io/openems/edge/meter/sunspec/AbstractSunSpecMeter.java +++ b/io.openems.edge.meter.sunspec/src/io/openems/edge/meter/sunspec/AbstractSunSpecMeter.java @@ -33,7 +33,7 @@ public abstract class AbstractSunSpecMeter extends AbstractOpenemsSunSpecCompone public AbstractSunSpecMeter(Map activeModels, io.openems.edge.common.channel.ChannelId[] firstInitialChannelIds, - io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) throws OpenemsException { + io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) { super(activeModels, firstInitialChannelIds, furtherInitialChannelIds); } diff --git a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/MeterWeidmueller525Impl.java b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/MeterWeidmueller525Impl.java index 0dfbec080a0..4dd38b5e0a3 100644 --- a/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/MeterWeidmueller525Impl.java +++ b/io.openems.edge.meter.weidmueller/src/io/openems/edge/meter/weidmueller/MeterWeidmueller525Impl.java @@ -81,7 +81,7 @@ public MeterType getMeterType() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC3ReadRegistersTask(19000, Priority.HIGH, // m(ElectricityMeter.ChannelId.VOLTAGE_L1, new FloatDoublewordElement(19000)), // diff --git a/io.openems.edge.meter.ziehl/src/io/openems/edge/meter/ziehl/efr4001ip/MeterZiehlEfr4001IpImpl.java b/io.openems.edge.meter.ziehl/src/io/openems/edge/meter/ziehl/efr4001ip/MeterZiehlEfr4001IpImpl.java index c4d4e7c398c..e6d852fcd0d 100644 --- a/io.openems.edge.meter.ziehl/src/io/openems/edge/meter/ziehl/efr4001ip/MeterZiehlEfr4001IpImpl.java +++ b/io.openems.edge.meter.ziehl/src/io/openems/edge/meter/ziehl/efr4001ip/MeterZiehlEfr4001IpImpl.java @@ -83,7 +83,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { final var startingRegister = 0x00B0; // 0x0000 for EFR4000IP final var startingRegisterFeedIn = 0x0156; // 0x0084 for EFR4000IP var modbusProtocol = new ModbusProtocol(this, // diff --git a/io.openems.edge.predictor.api/src/io/openems/edge/predictor/api/manager/PredictorManager.java b/io.openems.edge.predictor.api/src/io/openems/edge/predictor/api/manager/PredictorManager.java index b5c4499b7f0..dc9fdea5567 100644 --- a/io.openems.edge.predictor.api/src/io/openems/edge/predictor/api/manager/PredictorManager.java +++ b/io.openems.edge.predictor.api/src/io/openems/edge/predictor/api/manager/PredictorManager.java @@ -4,6 +4,7 @@ import io.openems.edge.common.channel.Doc; import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.predictor.api.prediction.Prediction; +import io.openems.edge.predictor.api.prediction.Predictor; public interface PredictorManager extends OpenemsComponent { @@ -29,8 +30,8 @@ public Doc doc() { * given {@link ChannelAddress}. * * @param channelAddress the {@link ChannelAddress} - * @return the {@link Prediction}; {@link Prediction#EMPTY_PREDICTION} if no Predictor - * matches the Channel-Address + * @return the {@link Prediction}; {@link Prediction#EMPTY_PREDICTION} if no + * Predictor matches the Channel-Address */ public Prediction getPrediction(ChannelAddress channelAddress); } diff --git a/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/PvInverterFroniusImpl.java b/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/PvInverterFroniusImpl.java index aaa2e9c45ee..1973b591a74 100644 --- a/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/PvInverterFroniusImpl.java +++ b/io.openems.edge.pvinverter.fronius/src/io/openems/edge/pvinverter/fronius/PvInverterFroniusImpl.java @@ -71,7 +71,7 @@ protected void setModbus(BridgeModbus modbus) { super.setModbus(modbus); } - public PvInverterFroniusImpl() throws OpenemsException { + public PvInverterFroniusImpl() { super(// ACTIVE_MODELS, // OpenemsComponent.ChannelId.values(), // diff --git a/io.openems.edge.pvinverter.kaco.blueplanet/src/io/openems/edge/pvinverter/kaco/blueplanet/PvInverterKacoBlueplanetImpl.java b/io.openems.edge.pvinverter.kaco.blueplanet/src/io/openems/edge/pvinverter/kaco/blueplanet/PvInverterKacoBlueplanetImpl.java index 68959240db1..da46c61fa7a 100644 --- a/io.openems.edge.pvinverter.kaco.blueplanet/src/io/openems/edge/pvinverter/kaco/blueplanet/PvInverterKacoBlueplanetImpl.java +++ b/io.openems.edge.pvinverter.kaco.blueplanet/src/io/openems/edge/pvinverter/kaco/blueplanet/PvInverterKacoBlueplanetImpl.java @@ -92,7 +92,7 @@ protected void setModbus(BridgeModbus modbus) { super.setModbus(modbus); } - public PvInverterKacoBlueplanetImpl() throws OpenemsException { + public PvInverterKacoBlueplanetImpl() { super(// ACTIVE_MODELS, // true /* enable manuel calculation of ActiveProductionEnergy */, // diff --git a/io.openems.edge.pvinverter.kostal/src/io/openems/edge/pvinverter/kostal/PvInverterKostalImpl.java b/io.openems.edge.pvinverter.kostal/src/io/openems/edge/pvinverter/kostal/PvInverterKostalImpl.java index 382e2dd37d7..e066097a383 100644 --- a/io.openems.edge.pvinverter.kostal/src/io/openems/edge/pvinverter/kostal/PvInverterKostalImpl.java +++ b/io.openems.edge.pvinverter.kostal/src/io/openems/edge/pvinverter/kostal/PvInverterKostalImpl.java @@ -73,7 +73,7 @@ protected void setModbus(BridgeModbus modbus) { super.setModbus(modbus); } - public PvInverterKostalImpl() throws OpenemsException { + public PvInverterKostalImpl() { super(// ACTIVE_MODELS, // OpenemsComponent.ChannelId.values(), // diff --git a/io.openems.edge.pvinverter.sma/src/io/openems/edge/pvinverter/sma/PvInverterSmaSunnyTripowerImpl.java b/io.openems.edge.pvinverter.sma/src/io/openems/edge/pvinverter/sma/PvInverterSmaSunnyTripowerImpl.java index aa0bf81cc43..1f08127e275 100644 --- a/io.openems.edge.pvinverter.sma/src/io/openems/edge/pvinverter/sma/PvInverterSmaSunnyTripowerImpl.java +++ b/io.openems.edge.pvinverter.sma/src/io/openems/edge/pvinverter/sma/PvInverterSmaSunnyTripowerImpl.java @@ -100,7 +100,7 @@ protected void setModbus(BridgeModbus modbus) { super.setModbus(modbus); } - public PvInverterSmaSunnyTripowerImpl() throws OpenemsException { + public PvInverterSmaSunnyTripowerImpl() { super(// ACTIVE_MODELS, // OpenemsComponent.ChannelId.values(), // diff --git a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/PvInverterSolarlogImpl.java b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/PvInverterSolarlogImpl.java index b8a9e29c21b..cf8c62c0da1 100644 --- a/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/PvInverterSolarlogImpl.java +++ b/io.openems.edge.pvinverter.solarlog/src/io/openems/edge/pvinverter/solarlog/PvInverterSolarlogImpl.java @@ -114,7 +114,7 @@ protected void deactivate() { } @Override - protected ModbusProtocol defineModbusProtocol() throws OpenemsException { + protected ModbusProtocol defineModbusProtocol() { return new ModbusProtocol(this, // new FC4ReadInputRegistersTask(3500, Priority.HIGH, m(PvInverterSolarlog.ChannelId.LAST_UPDATE_TIME, diff --git a/io.openems.edge.pvinverter.sunspec/src/io/openems/edge/pvinverter/sunspec/AbstractSunSpecPvInverter.java b/io.openems.edge.pvinverter.sunspec/src/io/openems/edge/pvinverter/sunspec/AbstractSunSpecPvInverter.java index b35a1e10f49..34ff14dc35d 100644 --- a/io.openems.edge.pvinverter.sunspec/src/io/openems/edge/pvinverter/sunspec/AbstractSunSpecPvInverter.java +++ b/io.openems.edge.pvinverter.sunspec/src/io/openems/edge/pvinverter/sunspec/AbstractSunSpecPvInverter.java @@ -57,6 +57,7 @@ private static enum InverterType { SINGLE_PHASE(S_101, S_111), // SPLIT_PHASE(S_102, S_112), // THREE_PHASE(S_103, S_113); + // TODO evaluate S701_ACType if block 701 is used private final List blocks; @@ -75,14 +76,14 @@ private InverterType(DefaultSunSpecModel... blocks) { public AbstractSunSpecPvInverter(Map activeModels, io.openems.edge.common.channel.ChannelId[] firstInitialChannelIds, - io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) throws OpenemsException { + io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) { this(activeModels, false, firstInitialChannelIds, furtherInitialChannelIds); } public AbstractSunSpecPvInverter(Map activeModels, boolean calculateActiveProductionEnergyManually, io.openems.edge.common.channel.ChannelId[] firstInitialChannelIds, - io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) throws OpenemsException { + io.openems.edge.common.channel.ChannelId[]... furtherInitialChannelIds) { super(activeModels, firstInitialChannelIds, furtherInitialChannelIds); this.calculateActiveProductionEnergyManually = calculateActiveProductionEnergyManually; this._setActiveConsumptionEnergy(0); @@ -322,7 +323,7 @@ protected void onSunSpecInitializationCompleted() { } @Override - protected void addBlock(int startAddress, SunSpecModel model, Priority priority) throws OpenemsException { + protected void addBlock(int startAddress, SunSpecModel model, Priority priority) { super.addBlock(startAddress, model, priority); // Can we evaluate the InverterType from this Block? diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorApp.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorApp.java index 6fbdd0d0d75..d3cbdc6051d 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorApp.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorApp.java @@ -5,12 +5,10 @@ import io.openems.edge.common.channel.Doc; import io.openems.edge.common.component.ClockProvider; import io.openems.edge.common.component.OpenemsComponent; -import io.openems.edge.common.jsonapi.JsonApi; import io.openems.edge.simulator.datasource.api.SimulatorDatasource; import io.openems.edge.timedata.api.Timedata; -public interface SimulatorApp - extends SimulatorDatasource, ClockProvider, OpenemsComponent, JsonApi, EventHandler, Timedata { +public interface SimulatorApp extends SimulatorDatasource, ClockProvider, OpenemsComponent, EventHandler, Timedata { public enum ChannelId implements io.openems.edge.common.channel.ChannelId { ; diff --git a/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorAppImpl.java b/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorAppImpl.java index ca6c1215434..8236fb6fa7b 100644 --- a/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorAppImpl.java +++ b/io.openems.edge.simulator/src/io/openems/edge/simulator/app/SimulatorAppImpl.java @@ -39,11 +39,8 @@ import com.google.gson.JsonPrimitive; import io.openems.common.exceptions.NotImplementedException; -import io.openems.common.exceptions.OpenemsError; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; -import io.openems.common.jsonrpc.base.JsonrpcRequest; -import io.openems.common.jsonrpc.base.JsonrpcResponseSuccess; import io.openems.common.jsonrpc.request.CreateComponentConfigRequest; import io.openems.common.jsonrpc.request.DeleteComponentConfigRequest; import io.openems.common.jsonrpc.request.UpdateComponentConfigRequest.Property; @@ -61,7 +58,10 @@ import io.openems.edge.common.component.OpenemsComponent; import io.openems.edge.common.cycle.Cycle; import io.openems.edge.common.event.EdgeEventConstants; -import io.openems.edge.common.jsonapi.JsonApi; +import io.openems.edge.common.jsonapi.ComponentJsonApi; +import io.openems.edge.common.jsonapi.EdgeGuards; +import io.openems.edge.common.jsonapi.EdgeKeys; +import io.openems.edge.common.jsonapi.JsonApiBuilder; import io.openems.edge.common.type.TypeUtils; import io.openems.edge.common.user.User; import io.openems.edge.simulator.app.ExecuteSimulationRequest.Profile; @@ -79,8 +79,8 @@ EdgeEventConstants.TOPIC_CYCLE_AFTER_PROCESS_IMAGE, // EdgeEventConstants.TOPIC_CYCLE_AFTER_WRITE // }) -public class SimulatorAppImpl extends AbstractOpenemsComponent - implements SimulatorApp, SimulatorDatasource, ClockProvider, OpenemsComponent, JsonApi, EventHandler, Timedata { +public class SimulatorAppImpl extends AbstractOpenemsComponent implements SimulatorApp, SimulatorDatasource, + ClockProvider, OpenemsComponent, EventHandler, Timedata, ComponentJsonApi { public static final String SINGLETON_SERVICE_PID = "Simulator.App"; public static final String SINGLETON_COMPONENT_ID = "_simulator"; @@ -157,18 +157,11 @@ protected void deactivate() { } @Override - public CompletableFuture handleJsonrpcRequest(User user, JsonrpcRequest request) - throws OpenemsNamedException { - user.assertRoleIsAtLeast("handleJsonrpcRequest", Role.ADMIN); - - switch (request.getMethod()) { - - case ExecuteSimulationRequest.METHOD: - return this.handleExecuteSimulationRequest(user, ExecuteSimulationRequest.from(request)); - - default: - throw OpenemsError.JSONRPC_UNHANDLED_METHOD.exception(request.getMethod()); - } + public void buildJsonApiRoutes(JsonApiBuilder builder) { + builder.handleRequest(ExecuteSimulationRequest.METHOD, def -> { + def.setGuards(EdgeGuards.roleIsAtleast(Role.ADMIN)); + }, call -> this.handleExecuteSimulationRequest(call.get(EdgeKeys.USER_KEY), + ExecuteSimulationRequest.from(call.getRequest())).get()); } /** @@ -189,7 +182,7 @@ private synchronized CompletableFuture handleExecuteS this.setCycleTime(AbstractWorker.ALWAYS_WAIT_FOR_TRIGGER_NEXT_RUN); // Create Ess.Power with disabled PID filter - this.componentManager.handleJsonrpcRequest(user, + this.componentManager.handleCreateComponentConfigRequest(user, new CreateComponentConfigRequest("Ess.Power", Arrays.asList(new Property("enablePid", false)))); // Create Components @@ -198,7 +191,7 @@ private synchronized CompletableFuture handleExecuteS this.logInfo(this.log, "Create Component [" + createRequest.getComponentId() + "] from [" + createRequest.getFactoryPid() + "]"); simulatorComponentIds.add(createRequest.getComponentId()); - this.componentManager.handleJsonrpcRequest(user, createRequest); + this.componentManager.handleCreateComponentConfigRequest(user, createRequest); } this.waitForComponentsToActivate(simulatorComponentIds); @@ -389,7 +382,7 @@ private void stopSimulation() { private void deleteComponent(User user, String componentId) throws OpenemsNamedException { this.logInfo(this.log, "Delete Component [" + componentId + "]"); var deleteComponentConfigRequest = new DeleteComponentConfigRequest(componentId); - this.componentManager.handleJsonrpcRequest(user, deleteComponentConfigRequest); + this.componentManager.handleDeleteComponentConfigRequest(user, deleteComponentConfigRequest); } /** diff --git a/io.openems.edge.solaredge/src/io/openems/edge/solaredge/gridmeter/SolarEdgeGridMeterImpl.java b/io.openems.edge.solaredge/src/io/openems/edge/solaredge/gridmeter/SolarEdgeGridMeterImpl.java index 0fb0dc646ca..fdb83a0ae8f 100644 --- a/io.openems.edge.solaredge/src/io/openems/edge/solaredge/gridmeter/SolarEdgeGridMeterImpl.java +++ b/io.openems.edge.solaredge/src/io/openems/edge/solaredge/gridmeter/SolarEdgeGridMeterImpl.java @@ -64,7 +64,7 @@ protected void setModbus(BridgeModbus modbus) { private Config config; - public SolarEdgeGridMeterImpl() throws OpenemsException { + public SolarEdgeGridMeterImpl() { super(// ACTIVE_MODELS, // OpenemsComponent.ChannelId.values(), // diff --git a/io.openems.edge.solaredge/src/io/openems/edge/solaredge/pvinverter/SolarEdgePvInverterImpl.java b/io.openems.edge.solaredge/src/io/openems/edge/solaredge/pvinverter/SolarEdgePvInverterImpl.java index 721120a5fdf..b8712b27a04 100644 --- a/io.openems.edge.solaredge/src/io/openems/edge/solaredge/pvinverter/SolarEdgePvInverterImpl.java +++ b/io.openems.edge.solaredge/src/io/openems/edge/solaredge/pvinverter/SolarEdgePvInverterImpl.java @@ -81,7 +81,7 @@ protected void setModbus(BridgeModbus modbus) { super.setModbus(modbus); } - public SolarEdgePvInverterImpl() throws OpenemsException { + public SolarEdgePvInverterImpl() { super(// ACTIVE_MODELS, // OpenemsComponent.ChannelId.values(), // diff --git a/io.openems.edge.timedata.api/src/io/openems/edge/timedata/api/Timedata.java b/io.openems.edge.timedata.api/src/io/openems/edge/timedata/api/Timedata.java index 36fe7b9595e..e581eb800a4 100644 --- a/io.openems.edge.timedata.api/src/io/openems/edge/timedata/api/Timedata.java +++ b/io.openems.edge.timedata.api/src/io/openems/edge/timedata/api/Timedata.java @@ -10,6 +10,7 @@ import com.google.gson.JsonElement; +import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.timedata.CommonTimedataService; import io.openems.common.types.ChannelAddress; @@ -41,6 +42,23 @@ public Doc doc() { */ public CompletableFuture> getLatestValue(ChannelAddress channelAddress); + /** + * Gets the latest known value for the given {@link ChannelAddress} of a not + * existing channel. + * + *

    + * Gets the latest known value even if the ChannelAddress is not longer + * existing. + * + * @param channelAddress the ChannelAddress to be queried + * @param unit unit + * @return the latest known value or Empty + */ + public default CompletableFuture> getLatestValueOfNotExistingChannel(ChannelAddress channelAddress, + Unit unit) { + return CompletableFuture.completedFuture(Optional.empty()); + } + /** * Gets the {@link Timeranges} to data which got not send. The not send data * gets determined with the notSendChannel and the lastResendTimestamp. diff --git a/io.openems.edge.timedata.api/src/io/openems/edge/timedata/api/utils/CalculateEnergyFromPower.java b/io.openems.edge.timedata.api/src/io/openems/edge/timedata/api/utils/CalculateEnergyFromPower.java index 1a38462b260..49d0100f04b 100644 --- a/io.openems.edge.timedata.api/src/io/openems/edge/timedata/api/utils/CalculateEnergyFromPower.java +++ b/io.openems.edge.timedata.api/src/io/openems/edge/timedata/api/utils/CalculateEnergyFromPower.java @@ -190,4 +190,17 @@ private void calculateEnergy() { // update 'cumulatedEnergy' this.component.channel(this.channelId).setNextValue(this.baseCumulatedEnergy); } + + /** + * Set baseEnergy manually. + * + *

    + * Set baseEnergy manually & go to CALCULATE_ENERGY_OPERATION + * + * @param baseCumulatedEnergy baseCumulatedEnergy + */ + public void setBaseEnergyManually(long baseCumulatedEnergy) { + this.baseCumulatedEnergy = baseCumulatedEnergy; + this.state = State.CALCULATE_ENERGY_OPERATION; + } } diff --git a/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/TimedataInfluxDbImpl.java b/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/TimedataInfluxDbImpl.java index d0b7c3f4b61..10517dc27a4 100644 --- a/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/TimedataInfluxDbImpl.java +++ b/io.openems.edge.timedata.influxdb/src/io/openems/edge/timedata/influxdb/TimedataInfluxDbImpl.java @@ -220,33 +220,34 @@ public SortedMap> queryResendData(Z // TODO implement this method return emptySortedMap(); } - + @Override public CompletableFuture> getLatestValue(ChannelAddress channelAddress) { - return CompletableFuture.supplyAsync(() -> { - try { - SortedMap sortedMap = this.influxConnector.queryLastData(Optional.empty(), channelAddress, this.config.measurement()); + return CompletableFuture.supplyAsync(() -> { + try { + SortedMap sortedMap = this.influxConnector.queryLastData(Optional.empty(), + channelAddress, this.config.measurement()); - if (sortedMap != null && !sortedMap.isEmpty() && sortedMap.containsKey(channelAddress)) { - JsonElement latestValue = sortedMap.get(channelAddress); + if (sortedMap != null && !sortedMap.isEmpty() && sortedMap.containsKey(channelAddress)) { + JsonElement latestValue = sortedMap.get(channelAddress); - // Check if it´s a number and can be converted to long - if (latestValue.isJsonPrimitive()) { - if (latestValue.getAsJsonPrimitive().isNumber()) { - return Optional.of(latestValue.getAsLong()); - } - } - } else { - // No data found - return Optional.empty(); - } - } catch (Exception e) { - this.log.error("Error getting latest value", e); - } - return Optional.empty(); - }); + // Check if it´s a number and can be converted to long + if (latestValue.isJsonPrimitive()) { + if (latestValue.getAsJsonPrimitive().isNumber()) { + return Optional.of(latestValue.getAsLong()); + } + } + } else { + // No data found + return Optional.empty(); + } + } catch (Exception e) { + this.log.error("Error getting latest value", e); + } + return Optional.empty(); + }); } - + @Override public Timeranges getResendTimeranges(ChannelAddress notSendChannel, long lastResendTimestamp) throws OpenemsNamedException { diff --git a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jReadHandler.java b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jReadHandler.java index abba6d9c364..6747b3b96d9 100644 --- a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jReadHandler.java +++ b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jReadHandler.java @@ -28,6 +28,7 @@ import com.google.gson.JsonNull; import com.google.gson.JsonPrimitive; +import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.exceptions.OpenemsException; import io.openems.common.timedata.Resolution; @@ -565,6 +566,48 @@ public CompletableFuture> getLatestValue(// }); } + /** + * Gets the latest known value for the given {@link ChannelAddress} of a not + * existing channel. + * + *

    + * Gets the latest known value even if the ChannelAddress is not longer + * existing. + * + * @param rrdDbId the id of the rrdb + * @param channelAddress the ChannelAddress to be queried + * @param unit unit + * @return the latest known value or Empty + */ + public CompletableFuture> getLatestValueOfNotExistingChannel(// + final String rrdDbId, // + final ChannelAddress channelAddress, // + final Unit unit // + ) { + return CompletableFuture.supplyAsync(() -> { + + try (var database = this.rrd4jSupplier.getExistingUpdatedRrdDb(rrdDbId, channelAddress, unit)) { + if (database == null) { + return Optional.empty(); + } + + // search for last value in robin + final var robin = database.getArchive(0).getRobin(0); + for (int i = robin.getSize() - 1; i >= 0; i--) { + final var value = robin.getValue(i); + if (Double.isNaN(value)) { + continue; + } + return Optional.of(value); + } + + return Optional.empty(); + } catch (Exception e) { + return Optional.empty(); + } + }); + } + private static double getFirstValueBefore(RrdDb database, long endTimestamp) throws IOException { final var archive = database.getArchive(0); if (archive.getStartTime() > endTimestamp) { diff --git a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jSupplier.java b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jSupplier.java index 8efc38568d3..fc0d4f2b50b 100644 --- a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jSupplier.java +++ b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/Rrd4jSupplier.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiFunction; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; @@ -40,7 +41,7 @@ ) public class Rrd4jSupplier { - private final Logger log = LoggerFactory.getLogger(this.getClass()); + private final Logger log = LoggerFactory.getLogger(Rrd4jSupplier.class); @Reference private VersionHandler versionHandler; @@ -48,15 +49,29 @@ public class Rrd4jSupplier { private final KeyLock keyLock = new KeyLock(); private final RrdBackendFactory factory; + // channelAddress, rrdDbId => file path + private final BiFunction fileValidator; + protected Rrd4jSupplier(// - final RrdBackendFactory factory // + final RrdBackendFactory factory, // + final BiFunction fileValidator // ) { this.factory = factory; + this.fileValidator = fileValidator; } @Activate public Rrd4jSupplier() { - this(new RrdRandomAccessFileBackendFactory()); + this(// + new RrdRandomAccessFileBackendFactory(), // + (t, u) -> { + final var file = getDbFile(t, u); + if (!file.exists()) { + return null; + } + return file.toURI().toString(); + } // + ); } /** @@ -123,7 +138,7 @@ public RrdDb getExistingUpdatedRrdDb(// */ public static ChannelDef getDsDefForChannel(final Unit channelUnit) { return switch (channelUnit) { - case AMPERE, AMPERE_HOURS, DEGREE_CELSIUS, DEZIDEGREE_CELSIUS, EUROS_PER_MEGAWATT_HOUR, HERTZ, HOUR, + case AMPERE, AMPERE_HOURS, DEGREE_CELSIUS, DEZIDEGREE_CELSIUS, MONEY_PER_MEGAWATT_HOUR, HERTZ, HOUR, KILOAMPERE_HOURS, KILOOHM, KILOVOLT_AMPERE, KILOVOLT_AMPERE_REACTIVE, KILOWATT, MICROOHM, MICROAMPERE, MICROVOLT, MILLIAMPERE_HOURS, MILLIAMPERE, MILLIHERTZ, MILLIOHM, MILLISECONDS, MILLIVOLT, MILLIWATT, MINUTE, NONE, WATT, VOLT, VOLT_AMPERE, VOLT_AMPERE_REACTIVE, WATT_HOURS_BY_WATT_PEAK, OHM, SECONDS, @@ -148,8 +163,8 @@ private RrdDb getExistingRrdDb(// final ChannelAddress channelAddress, // final String rrdDbId // ) { - var file = getDbFile(channelAddress, rrdDbId); - if (!file.exists()) { + final var filePath = this.fileValidator.apply(channelAddress, rrdDbId); + if (filePath == null) { return null; } try { @@ -158,7 +173,7 @@ private RrdDb getExistingRrdDb(// // .setPool(RrdDbPool.getInstance()) // // ^^ is not used anymore because of caching // problems when overwriting the old database file - .setPath(file.toURI()) // + .setPath(filePath) // .build(); } catch (IOException e) { this.log.error("Unable to open existing RrdDb", e); @@ -334,24 +349,19 @@ public static double[] postProcessData(FetchData data, long resolution) "Requested resolution [" + resolution + "] is not dividable by RRD4j Step [" + step + "]"); } var merge = (int) (resolution / step); - var buffer = new double[merge]; - for (var i = 1; i < input.length; i += merge) { + for (int i = 0; i < result.length; i++) { + var buffer = new double[merge]; for (var j = 0; j < merge; j++) { - if (i + j < input.length) { - buffer[j] = input[i + j]; + final var inputIndex = (i * merge) + j; + if (inputIndex < input.length) { + buffer[j] = input[inputIndex]; } else { buffer[j] = Double.NaN; } } - // put in result; avoid index rounding error - var resultIndex = (i - 1) / merge; - if (resultIndex >= result.length) { - break; - } - result[resultIndex] = TypeUtils.average(buffer); + result[i] = TypeUtils.average(buffer); } - } else if (step > resolution) { // Split each entry to multiple values var resultTimestamp = 0; diff --git a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/TimedataRrd4jImpl.java b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/TimedataRrd4jImpl.java index e1d6898bcfd..81fd3d0e9e6 100644 --- a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/TimedataRrd4jImpl.java +++ b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/TimedataRrd4jImpl.java @@ -19,6 +19,7 @@ import com.google.gson.JsonElement; +import io.openems.common.channel.Unit; import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; import io.openems.common.timedata.Resolution; import io.openems.common.types.ChannelAddress; @@ -138,6 +139,12 @@ public CompletableFuture> getLatestValue(ChannelAddress channel return this.readHandler.getLatestValue(this.id(), channelAddress); } + @Override + public CompletableFuture> getLatestValueOfNotExistingChannel(ChannelAddress channelAddress, + Unit unit) { + return this.readHandler.getLatestValueOfNotExistingChannel(this.id(), channelAddress, unit); + } + @Override public void handleEvent(Event event) { if (!this.isEnabled()) { diff --git a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/version/VersionHandler.java b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/version/VersionHandler.java index f06ad16ad66..c6c598ead39 100644 --- a/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/version/VersionHandler.java +++ b/io.openems.edge.timedata.rrd4j/src/io/openems/edge/timedata/rrd4j/version/VersionHandler.java @@ -44,12 +44,17 @@ public static int getVersion(RrdDb db) throws IOException { private final List versions = new ArrayList<>(); + /** + * Binds a {@link Version} to this current handler. + * + * @param version the {@link Version} to bind + */ @Reference(// policy = ReferencePolicy.DYNAMIC, // cardinality = ReferenceCardinality.MULTIPLE, // bind = "bindVersion", unbind = "unbindVersion" // ) - protected void bindVersion(Version version) { + public void bindVersion(Version version) { // make sure the versions list is sorted by the version number ascending final var insertIndex = Collections.binarySearch(this.versions, version, Version.numberComparator()); if (insertIndex < 0) { @@ -57,7 +62,12 @@ protected void bindVersion(Version version) { } } - protected void unbindVersion(Version version) { + /** + * Unbinds a {@link Version} from this current handler. + * + * @param version the {@link Version} to unbind + */ + public void unbindVersion(Version version) { this.versions.remove(version); } diff --git a/io.openems.edge.timedata.rrd4j/test/io/openems/edge/timedata/rrd4j/Rrd4jReadHandlerTest.java b/io.openems.edge.timedata.rrd4j/test/io/openems/edge/timedata/rrd4j/Rrd4jReadHandlerTest.java index c94a79f0583..635a3035742 100644 --- a/io.openems.edge.timedata.rrd4j/test/io/openems/edge/timedata/rrd4j/Rrd4jReadHandlerTest.java +++ b/io.openems.edge.timedata.rrd4j/test/io/openems/edge/timedata/rrd4j/Rrd4jReadHandlerTest.java @@ -1,43 +1,94 @@ package io.openems.edge.timedata.rrd4j; +import static java.util.stream.Collectors.toMap; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.Arrays; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; import org.junit.Before; import org.junit.Test; import org.rrd4j.core.RrdBackendFactory; +import org.rrd4j.core.RrdDb; import org.rrd4j.core.RrdMemoryBackendFactory; +import com.google.gson.JsonElement; +import com.google.gson.JsonPrimitive; + import io.openems.common.channel.Unit; +import io.openems.common.exceptions.OpenemsError.OpenemsNamedException; +import io.openems.common.timedata.Resolution; +import io.openems.common.types.ChannelAddress; +import io.openems.common.types.OpenemsType; +import io.openems.common.utils.ReflectionUtils; +import io.openems.edge.common.channel.Doc; +import io.openems.edge.common.component.OpenemsComponent; +import io.openems.edge.common.test.AbstractDummyOpenemsComponent; +import io.openems.edge.common.test.DummyComponentManager; +import io.openems.edge.common.type.Tuple; import io.openems.edge.timedata.rrd4j.version.Version.CreateDatabaseConfig; import io.openems.edge.timedata.rrd4j.version.Version3Test; +import io.openems.edge.timedata.rrd4j.version.VersionHandler; public class Rrd4jReadHandlerTest { // starts at 1. January 2020 00:00:00 private static final Instant START = Instant.ofEpochSecond(1577836800L); + private final String rrdbId = "rrdbId"; private RrdBackendFactory factory; + private RrdDb db; + private Rrd4jReadHandler readHandler; + private DummyComponent dummyComponent; @Before public void setUp() throws Exception { this.factory = new RrdMemoryBackendFactory(); - } - - @Test - public void testGetArchivesSortedByArcStep() throws Exception { final var version3 = Version3Test.createDummyVersion3(); - final var db = version3.createNewDb(new CreateDatabaseConfig(// - "rrdbId", // - Unit.WATT_HOURS, // - "path", // - START.getEpochSecond(), // + this.db = version3.createNewDb(new CreateDatabaseConfig(// + this.rrdbId, // + Unit.WATT, // + "comp0/DummyChannel", // + START.getEpochSecond() - 1, // this.factory, // null // )); - final var sorted = Rrd4jReadHandler.getArchivesSortedByArcStep(db); + final var robin = this.db.getArchive(0).getRobin(0); + for (int i = 0; i < robin.getSize(); i++) { + final var value = i * 100; + this.db.createSample(START.plus(5 * i, ChronoUnit.MINUTES).getEpochSecond()) // + .setValues(value) // + .update(); + } + + this.readHandler = new Rrd4jReadHandler(); + final var dcm = new DummyComponentManager(); + this.dummyComponent = new DummyComponent("comp0"); + dcm.addComponent(this.dummyComponent); + final var rrd4jSupplier = new Rrd4jSupplier(this.factory, (t, u) -> { + return t.toString(); + }); + + final var versionHandler = new VersionHandler(); + versionHandler.bindVersion(version3); + + ReflectionUtils.setAttribute(Rrd4jReadHandler.class, this.readHandler, "componentManager", dcm); + ReflectionUtils.setAttribute(Rrd4jReadHandler.class, this.readHandler, "rrd4jSupplier", rrd4jSupplier); + ReflectionUtils.setAttribute(Rrd4jSupplier.class, rrd4jSupplier, "versionHandler", versionHandler); + + } + + @Test + public void testGetArchivesSortedByArcStep() throws Exception { + final var sorted = Rrd4jReadHandler.getArchivesSortedByArcStep(this.db); long lastStepSize = 0L; for (var archive : sorted) { assertTrue("The last step size should be lower than the next step size.", @@ -47,4 +98,121 @@ public void testGetArchivesSortedByArcStep() throws Exception { } + @Test + public void testQueryHistoricDataWithResolution1minutes() throws Exception { + assertEquals(values(// + Tuple.of(START.plus(0, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 0), // + Tuple.of(START.plus(1, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 0), // + Tuple.of(START.plus(2, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 0), // + Tuple.of(START.plus(3, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 0), // + Tuple.of(START.plus(4, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 0), // + Tuple.of(START.plus(5, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 100), // + Tuple.of(START.plus(6, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 100), // + Tuple.of(START.plus(7, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 100), // + Tuple.of(START.plus(8, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 100), // + Tuple.of(START.plus(9, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 100), // + Tuple.of(START.plus(10, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 200), // + Tuple.of(START.plus(11, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 200), // + Tuple.of(START.plus(12, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 200), // + Tuple.of(START.plus(13, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 200), // + Tuple.of(START.plus(14, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 200), // + Tuple.of(START.plus(15, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 300), // + Tuple.of(START.plus(16, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 300), // + Tuple.of(START.plus(17, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 300), // + Tuple.of(START.plus(18, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 300), // + Tuple.of(START.plus(19, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 300), // + Tuple.of(START.plus(20, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 400), // + Tuple.of(START.plus(21, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 400), // + Tuple.of(START.plus(22, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 400), // + Tuple.of(START.plus(23, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 400), // + Tuple.of(START.plus(24, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 400), // + Tuple.of(START.plus(25, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 500), // + Tuple.of(START.plus(26, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 500), // + Tuple.of(START.plus(27, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 500), // + Tuple.of(START.plus(28, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 500), // + Tuple.of(START.plus(29, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 500) // + ), this.query(new Resolution(1, ChronoUnit.MINUTES))); + } + + @Test + public void testQueryHistoricDataWithResolution5minutes() throws Exception { + assertEquals(values(// + Tuple.of(START.plus(0, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 0), // + Tuple.of(START.plus(5, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 100), // + Tuple.of(START.plus(10, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 200), // + Tuple.of(START.plus(15, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 300), // + Tuple.of(START.plus(20, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 400), // + Tuple.of(START.plus(25, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 500) // + ), this.query(new Resolution(5, ChronoUnit.MINUTES))); + } + + @Test + public void testQueryHistoricDataWithResolution10minutes() throws Exception { + assertEquals(values(// + Tuple.of(START.plus(0, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 50), // + Tuple.of(START.plus(10, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 250), // + Tuple.of(START.plus(20, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 450) // + ), this.query(new Resolution(10, ChronoUnit.MINUTES))); + } + + @Test + public void testQueryHistoricDataWithResolution15minutes() throws Exception { + assertEquals(values(// + Tuple.of(START.plus(0, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 100), // + Tuple.of(START.plus(15, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), 400) // + ), this.query(new Resolution(15, ChronoUnit.MINUTES))); + } + + private SortedMap> query(Resolution resolution) + throws IllegalArgumentException, OpenemsNamedException { + return this.readHandler.queryHistoricData(this.rrdbId, // + START.atZone(ZoneId.of("UTC")), // + START.plus(30, ChronoUnit.MINUTES).atZone(ZoneId.of("UTC")), // + Set.of(this.dummyComponent.channel(DummyComponent.ChannelId.DUMMY_CHANNEL).address()), // + resolution, false); + } + + @SafeVarargs + private SortedMap> values( + Tuple... values) { + return Arrays.stream(values) // + .collect(toMap(Tuple::a, t -> { + final var valueMap = new TreeMap(); + valueMap.put(this.dummyComponent.channel(DummyComponent.ChannelId.DUMMY_CHANNEL).address(), + new JsonPrimitive(t.b())); + return valueMap; + }, (t, u) -> u, TreeMap::new)); + } + + private class DummyComponent extends AbstractDummyOpenemsComponent implements OpenemsComponent { + + public enum ChannelId implements io.openems.edge.common.channel.ChannelId { + DUMMY_CHANNEL(Doc.of(OpenemsType.INTEGER)); // + + private final Doc doc; + + private ChannelId(Doc doc) { + this.doc = doc; + } + + @Override + public Doc doc() { + return this.doc; + } + } + + public DummyComponent(String id) { + super(id, // + OpenemsComponent.ChannelId.values(), // + ChannelId.values() // + ); + } + + @Override + protected DummyComponent self() { + return this; + } + + } + } diff --git a/io.openems.edge.timedata.rrd4j/test/io/openems/edge/timedata/rrd4j/TimedataRrd4jImplTest.java b/io.openems.edge.timedata.rrd4j/test/io/openems/edge/timedata/rrd4j/TimedataRrd4jImplTest.java index 52a2c9d45ee..980994dfb48 100644 --- a/io.openems.edge.timedata.rrd4j/test/io/openems/edge/timedata/rrd4j/TimedataRrd4jImplTest.java +++ b/io.openems.edge.timedata.rrd4j/test/io/openems/edge/timedata/rrd4j/TimedataRrd4jImplTest.java @@ -87,9 +87,9 @@ public void testMerge() throws IOException, URISyntaxException { database.close(); assertEquals(12, result.length); // 3 hours * 4 entries/per hour (15 minutes) = 12 - assertEquals(8.0, result[0], 0.1); - assertEquals(23.0, result[1], 0.1); - assertEquals(38.0, result[2], 0.1); + assertEquals((0 + 3 + 8) / 3.0, result[0], 0.1); + assertEquals((13 + 18 + 23) / 3.0, result[1], 0.1); + assertEquals((28 + 33 + 38) / 3.0, result[2], 0.1); } /** diff --git a/io.openems.edge.timeofusetariff.awattar/src/io/openems/edge/timeofusetariff/awattar/TimeOfUseTariffAwattarImpl.java b/io.openems.edge.timeofusetariff.awattar/src/io/openems/edge/timeofusetariff/awattar/TimeOfUseTariffAwattarImpl.java index 90955376748..21a489c625d 100644 --- a/io.openems.edge.timeofusetariff.awattar/src/io/openems/edge/timeofusetariff/awattar/TimeOfUseTariffAwattarImpl.java +++ b/io.openems.edge.timeofusetariff.awattar/src/io/openems/edge/timeofusetariff/awattar/TimeOfUseTariffAwattarImpl.java @@ -149,7 +149,7 @@ public static TimeOfUsePrices parsePrices(String jsonData) throws OpenemsNamedEx .ofInstant(Instant.ofEpochMilli(getAsLong(element, "start_timestamp")), // ZoneId.systemDefault()) .truncatedTo(ChronoUnit.HOURS); - + // Adding the values in the Map. result.put(startTimeStamp, marketPrice); result.put(startTimeStamp.plusMinutes(15), marketPrice); diff --git a/io.openems.wrapper/bnd.bnd b/io.openems.wrapper/bnd.bnd index 5cc36edbe38..a4088ac1113 100644 --- a/io.openems.wrapper/bnd.bnd +++ b/io.openems.wrapper/bnd.bnd @@ -10,10 +10,10 @@ Bundle-Description: This wraps external java libraries that do not have OSGi hea com.influxdb:flux-dsl;version='6.12.0',\ com.squareup.okhttp3:logging-interceptor;version='4.12.0',\ com.squareup.okhttp3:okhttp;version='4.12.0',\ - com.squareup.retrofit2:retrofit;version='2.10.0',\ - com.squareup.retrofit2:converter-gson;version='2.10.0',\ - com.squareup.retrofit2:converter-scalars;version='2.10.0',\ - com.squareup.retrofit2:adapter-rxjava3;version='2.10.0',\ + com.squareup.retrofit2:retrofit;version='2.11.0',\ + com.squareup.retrofit2:converter-gson;version='2.11.0',\ + com.squareup.retrofit2:converter-scalars;version='2.11.0',\ + com.squareup.retrofit2:adapter-rxjava3;version='2.11.0',\ com.sun.activation.javax.activation;version='1.2.0',\ eu.chargetime.ocpp:OCPP-J;version='1.0.2',\ eu.chargetime.ocpp:common;version='1.0.2',\ @@ -24,6 +24,6 @@ Bundle-Description: This wraps external java libraries that do not have OSGi hea com.google.gson;version='2.10.1',\ de.bytefish:pgbulkinsert;version='8.1.3',\ fr.turri:aXMLRPC;version='1.13.0',\ - org.dhatim:fastexcel;version='0.17.0',\ + org.dhatim:fastexcel;version='0.18.0',\ org.eclipse.paho.mqttv5.client;version='1.2.5',\ org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm;version='1.7.3',\ diff --git a/io.openems.wrapper/fastexcel.bnd b/io.openems.wrapper/fastexcel.bnd index efe0ad1737b..ab7f758a06e 100644 --- a/io.openems.wrapper/fastexcel.bnd +++ b/io.openems.wrapper/fastexcel.bnd @@ -1,9 +1,9 @@ Bundle-Name: fastexcel Bundle-DocURL: https://github.com/dhatim/fastexcel Bundle-License: https://opensource.org/licenses/Apache-2.0 -Bundle-Version: 0.17.0 +Bundle-Version: 0.18.0 -Include-Resource: @fastexcel-0.17.0.jar +Include-Resource: @fastexcel-0.18.0.jar -dsannotations: * diff --git a/io.openems.wrapper/retrofit-adapter-rxjava3.bnd b/io.openems.wrapper/retrofit-adapter-rxjava3.bnd index 696523a3dc8..bd31abfc460 100644 --- a/io.openems.wrapper/retrofit-adapter-rxjava3.bnd +++ b/io.openems.wrapper/retrofit-adapter-rxjava3.bnd @@ -2,7 +2,7 @@ # NOTE: At runtime Retrofit throws a not so nice warning: # # WARNING: An illegal reflective access operation has occurred -# WARNING: Illegal reflective access by retrofit2.Platform (file:.../.gradle/caches/.../retrofit-2.10.0.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int) +# WARNING: Illegal reflective access by retrofit2.Platform (file:.../.gradle/caches/.../retrofit-2.11.0.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int) # WARNING: Please consider reporting this to the maintainers of retrofit2.Platform # WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations # WARNING: All illegal access operations will be denied in a future release @@ -14,10 +14,10 @@ Bundle-Name: retrofit-adapter-rxjava3 Bundle-Description: A type-safe HTTP client for Android and Java Bundle-DocURL: https://github.com/google/gson Bundle-License: https://opensource.org/licenses/Apache-2.0 -Bundle-Version: 2.10.0 +Bundle-Version: 2.11.0 Include-Resource: \ - @adapter-rxjava3-2.10.0.jar,\ + @adapter-rxjava3-2.11.0.jar,\ Export-Package: \ retrofit2.adapter.rxjava3,\ diff --git a/io.openems.wrapper/retrofit-converter-gson.bnd b/io.openems.wrapper/retrofit-converter-gson.bnd index 30433e39e8a..e7ae9564875 100644 --- a/io.openems.wrapper/retrofit-converter-gson.bnd +++ b/io.openems.wrapper/retrofit-converter-gson.bnd @@ -2,10 +2,10 @@ Bundle-Name: retrofit-converter-gson Bundle-Description: A type-safe HTTP client for Android and Java Bundle-DocURL: https://github.com/google/gson Bundle-License: https://opensource.org/licenses/Apache-2.0 -Bundle-Version: 2.10.0 +Bundle-Version: 2.11.0 Include-Resource: \ - @converter-gson-2.10.0.jar,\ + @converter-gson-2.11.0.jar,\ Export-Package: \ retrofit2.converter.gson,\ diff --git a/io.openems.wrapper/retrofit-converter-scalars.bnd b/io.openems.wrapper/retrofit-converter-scalars.bnd index f0983200c5c..0ddc1c29c7b 100644 --- a/io.openems.wrapper/retrofit-converter-scalars.bnd +++ b/io.openems.wrapper/retrofit-converter-scalars.bnd @@ -2,10 +2,10 @@ Bundle-Name: retrofit-converter-scalars Bundle-Description: A type-safe HTTP client for Android and Java Bundle-DocURL: https://github.com/google/gson Bundle-License: https://opensource.org/licenses/Apache-2.0 -Bundle-Version: 2.10.0 +Bundle-Version: 2.11.0 Include-Resource: \ - @converter-scalars-2.10.0.jar,\ + @converter-scalars-2.11.0.jar,\ Export-Package: \ retrofit2.converter.scalars,\ diff --git a/io.openems.wrapper/retrofit2.bnd b/io.openems.wrapper/retrofit2.bnd index a0b40ad3890..bb1a32d18ad 100644 --- a/io.openems.wrapper/retrofit2.bnd +++ b/io.openems.wrapper/retrofit2.bnd @@ -2,7 +2,7 @@ # NOTE: At runtime Retrofit throws a not so nice warning: # # WARNING: An illegal reflective access operation has occurred -# WARNING: Illegal reflective access by retrofit2.Platform (file:.../.gradle/caches/.../retrofit-2.10.0.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int) +# WARNING: Illegal reflective access by retrofit2.Platform (file:.../.gradle/caches/.../retrofit-2.11.0.jar) to constructor java.lang.invoke.MethodHandles$Lookup(java.lang.Class,int) # WARNING: Please consider reporting this to the maintainers of retrofit2.Platform # WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations # WARNING: All illegal access operations will be denied in a future release @@ -14,10 +14,10 @@ Bundle-Name: retrofit Bundle-Description: A type-safe HTTP client for Android and Java Bundle-DocURL: https://github.com/google/gson Bundle-License: https://opensource.org/licenses/Apache-2.0 -Bundle-Version: 2.10.0 +Bundle-Version: 2.11.0 Include-Resource: \ - @retrofit-2.10.0.jar,\ + @retrofit-2.11.0.jar,\ Export-Package: \ retrofit2,\ diff --git a/tools/common.sh b/tools/common.sh index f3f937fa62a..598a8341fb5 100644 --- a/tools/common.sh +++ b/tools/common.sh @@ -53,10 +53,10 @@ common_update_version_in_code() { sed --in-place "s#\(VERSION_DEV_BUILD_TIME = \)\"\(.*\)\";#\1\"$VERSION_DEV_BUILD_TIME\";#" $SRC_OPENEMS_CONSTANTS echo "## Update $SRC_PACKAGE_JSON" - sed --in-place "s#^\( \"version\": \"\).*\(\".*$\)#\1$VERSION\2#" $SRC_PACKAGE_JSON + sed --in-place "s#^\( \"version\": \"\).*\(\".*$\)#\1$VERSION\2#" $SRC_PACKAGE_JSON echo "## Update $SRC_PACKAGE_LOCK_JSON" - sed --in-place "s#^\( \"version\": \"\).*\(\".*$\)#\1$VERSION\2#" $SRC_PACKAGE_LOCK_JSON + sed --in-place "s#^\( \"version\": \"\).*\(\".*$\)#\1$VERSION\2#" $SRC_PACKAGE_LOCK_JSON echo "## Update $SRC_CHANGELOG_CONSTANTS" sed --in-place "s#\(UI_VERSION = \"\).*\(\";\)#\1$VERSION\2#" $SRC_CHANGELOG_CONSTANTS diff --git a/tools/prepare-commit.sh b/tools/prepare-commit.sh index 2f09b2e951b..2f7db41cca9 100755 --- a/tools/prepare-commit.sh +++ b/tools/prepare-commit.sh @@ -194,3 +194,15 @@ head -n $(grep -n '\-runbundles:' "$bndrun.new" | grep -Eo '^[^:]+' | head -n1) rm "$bndrun.new" ./gradlew resolve.BackendApp +# Build + test UI +cd ui +npm install +node_modules/.bin/ng lint --fix +node_modules/.bin/tsc +node_modules/.bin/tsc-strict +node_modules/.bin/ng build -c "openems,openems-backend-prod,prod" +npm run test -- --no-watch --no-progress --browsers=ChromeHeadlessCI +cd .. + +echo +echo "Finished" diff --git a/ui/.vscode/settings.json b/ui/.vscode/settings.json index c854b790a8b..ca17a9ba2dd 100644 --- a/ui/.vscode/settings.json +++ b/ui/.vscode/settings.json @@ -22,5 +22,14 @@ "files.eol": "\n", "files.insertFinalNewline": true, "editor.defaultFormatter": "dbaeumer.vscode-eslint", - "eslint.format.enable": true + "eslint.format.enable": true, + "[typescript]": { + "editor.defaultFormatter": "vscode.typescript-language-features" + }, + "[jsonc]": { + "editor.defaultFormatter": "vscode.json-language-features" + }, + "[json]": { + "editor.defaultFormatter": "vscode.json-language-features" + } } diff --git a/ui/package-lock.json b/ui/package-lock.json index ffd2d30c97d..4b739a860c4 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,17026 +1,17368 @@ { - "name": "openems-ui", - "version": "2024.4.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "openems-ui", - "version": "2024.4.0-SNAPSHOT", - "license": "AGPL-3.0", - "dependencies": { - "@angular/animations": "~15.2.9", - "@angular/cdk": "~15.2.9", - "@angular/common": "~15.2.9", - "@angular/core": "~15.2.9", - "@angular/forms": "~15.2.9", - "@angular/platform-browser": "~15.2.9", - "@angular/platform-browser-dynamic": "~15.2.9", - "@angular/router": "~15.2.9", - "@angular/service-worker": "~15.2.9", - "@ionic/angular": "^6.7.4", - "@ngx-formly/core": "^6.1.7", - "@ngx-formly/ionic": "^6.1.7", - "@ngx-formly/schematics": "^6.1.7", - "@ngx-translate/core": "^14.0.0", - "angular-mydatepicker": "^0.11.5", - "chart.js": "^4.4.0", - "chartjs-adapter-date-fns": "^3.0.0", - "chartjs-plugin-zoom": "^2.0.1", - "classlist.js": "^1.1.20150312", - "compare-versions": "^6.1.0", - "d3": "^7.9.0", - "date-fns": "^2.30.0", - "file-saver-es": "^2.0.5", - "ng2-charts": "4.1.1", - "ngx-cookie-service": "^15.0.0", - "ngx-spinner": "^15.0.1", - "roboto-fontface": "^0.10.0", - "rxjs": "~6.6.7", - "tslib": "^2.5.0", - "uuid": "^9.0.0", - "zone.js": "~0.13.0" - }, - "devDependencies": { - "@angular-devkit/build-angular": "^15.2.8", - "@angular-eslint/builder": "~15.2.1", - "@angular-eslint/eslint-plugin": "~15.2.1", - "@angular-eslint/eslint-plugin-template": "~15.2.1", - "@angular-eslint/template-parser": "~15.2.1", - "@angular/cli": "~15.2.8", - "@angular/compiler": "~15.2.9", - "@angular/compiler-cli": "~15.2.9", - "@angular/language-service": "~15.2.9", - "@ionic/angular-toolkit": "^7.0.0", - "@types/jasmine": "~4.3.2", - "@types/jasminewd2": "~2.0.10", - "@types/node": "^20.2.5", - "@types/uuid": "^9.0.2", - "@typescript-eslint/eslint-plugin": "^6.2.0", - "@typescript-eslint/parser": "6.2.0", - "eslint": "^8.41.0", - "eslint-plugin-import": "2.27.5", - "eslint-plugin-jsdoc": "45.0.0", - "eslint-plugin-prefer-arrow": "1.2.3", - "eslint-plugin-unused-imports": "^3.1.0", - "jasmine-core": "~4.5.0", - "jasmine-spec-reporter": "~7.0.0", - "karma": "~6.4.2", - "karma-chrome-launcher": "~3.2.0", - "karma-coverage": "~2.2.0", - "karma-coverage-istanbul-reporter": "~3.0.3", - "karma-jasmine": "~5.1.0", - "karma-jasmine-html-reporter": "^2.0.0", - "protractor": "~7.0.0", - "ts-node": "~10.9.1", - "typescript": "~4.9.5" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@angular-devkit/architect": { - "version": "0.1502.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.9.tgz", - "integrity": "sha512-CFn+LbtYeLG7WqO+BBSjogl764StHpwgfJnNAXQ/3UouUktZ92z4lxhUm0PwIPb5k0lILsf81ubcS1vzwoXEEg==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "15.2.9", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-angular": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-15.2.9.tgz", - "integrity": "sha512-djOo2Q22zLrxPccSbINz93hD+pES/nNPoze4Ys/0IdtMlLmxO/YGsA+FG5eNeNAf2jK/JRoNydaYOh7XpGoCzA==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "2.2.0", - "@angular-devkit/architect": "0.1502.9", - "@angular-devkit/build-webpack": "0.1502.9", - "@angular-devkit/core": "15.2.9", - "@babel/core": "7.20.12", - "@babel/generator": "7.20.14", - "@babel/helper-annotate-as-pure": "7.18.6", - "@babel/helper-split-export-declaration": "7.18.6", - "@babel/plugin-proposal-async-generator-functions": "7.20.7", - "@babel/plugin-transform-async-to-generator": "7.20.7", - "@babel/plugin-transform-runtime": "7.19.6", - "@babel/preset-env": "7.20.2", - "@babel/runtime": "7.20.13", - "@babel/template": "7.20.7", - "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "15.2.9", - "ansi-colors": "4.1.3", - "autoprefixer": "10.4.13", - "babel-loader": "9.1.2", - "babel-plugin-istanbul": "6.1.1", - "browserslist": "4.21.5", - "cacache": "17.0.4", - "chokidar": "3.5.3", - "copy-webpack-plugin": "11.0.0", - "critters": "0.0.16", - "css-loader": "6.7.3", - "esbuild-wasm": "0.17.8", - "glob": "8.1.0", - "https-proxy-agent": "5.0.1", - "inquirer": "8.2.4", - "jsonc-parser": "3.2.0", - "karma-source-map-support": "1.4.0", - "less": "4.1.3", - "less-loader": "11.1.0", - "license-webpack-plugin": "4.0.2", - "loader-utils": "3.2.1", - "magic-string": "0.29.0", - "mini-css-extract-plugin": "2.7.2", - "open": "8.4.1", - "ora": "5.4.1", - "parse5-html-rewriting-stream": "7.0.0", - "piscina": "3.2.0", - "postcss": "8.4.21", - "postcss-loader": "7.0.2", - "resolve-url-loader": "5.0.0", - "rxjs": "6.6.7", - "sass": "1.58.1", - "sass-loader": "13.2.0", - "semver": "7.5.3", - "source-map-loader": "4.0.1", - "source-map-support": "0.5.21", - "terser": "5.16.3", - "text-table": "0.2.0", - "tree-kill": "1.2.2", - "tslib": "2.5.0", - "webpack": "5.76.1", - "webpack-dev-middleware": "6.0.1", - "webpack-dev-server": "4.11.1", - "webpack-merge": "5.8.0", - "webpack-subresource-integrity": "5.1.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "optionalDependencies": { - "esbuild": "0.17.8" - }, - "peerDependencies": { - "@angular/compiler-cli": "^15.0.0", - "@angular/localize": "^15.0.0", - "@angular/platform-server": "^15.0.0", - "@angular/service-worker": "^15.0.0", - "karma": "^6.3.0", - "ng-packagr": "^15.0.0", - "protractor": "^7.0.0", - "tailwindcss": "^2.0.0 || ^3.0.0", - "typescript": ">=4.8.2 <5.0" - }, - "peerDependenciesMeta": { - "@angular/localize": { - "optional": true - }, - "@angular/platform-server": { - "optional": true - }, - "@angular/service-worker": { - "optional": true - }, - "karma": { - "optional": true - }, - "ng-packagr": { - "optional": true - }, - "protractor": { - "optional": true - }, - "tailwindcss": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/esbuild": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.8.tgz", - "integrity": "sha512-g24ybC3fWhZddZK6R3uD2iF/RIPnRpwJAqLov6ouX3hMbY4+tKolP0VMF3zuIYCaXun+yHwS5IPQ91N2BT191g==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.17.8", - "@esbuild/android-arm64": "0.17.8", - "@esbuild/android-x64": "0.17.8", - "@esbuild/darwin-arm64": "0.17.8", - "@esbuild/darwin-x64": "0.17.8", - "@esbuild/freebsd-arm64": "0.17.8", - "@esbuild/freebsd-x64": "0.17.8", - "@esbuild/linux-arm": "0.17.8", - "@esbuild/linux-arm64": "0.17.8", - "@esbuild/linux-ia32": "0.17.8", - "@esbuild/linux-loong64": "0.17.8", - "@esbuild/linux-mips64el": "0.17.8", - "@esbuild/linux-ppc64": "0.17.8", - "@esbuild/linux-riscv64": "0.17.8", - "@esbuild/linux-s390x": "0.17.8", - "@esbuild/linux-x64": "0.17.8", - "@esbuild/netbsd-x64": "0.17.8", - "@esbuild/openbsd-x64": "0.17.8", - "@esbuild/sunos-x64": "0.17.8", - "@esbuild/win32-arm64": "0.17.8", - "@esbuild/win32-ia32": "0.17.8", - "@esbuild/win32-x64": "0.17.8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/@angular-devkit/build-webpack": { - "version": "0.1502.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1502.9.tgz", - "integrity": "sha512-VzMXoZjrbL1XlcSegqpZCBDbVvKFGPs3cKp4bXDD5ht95jcCyJPk5FA/wrh0pGGwbOF8ae/XOWFcPRzctC35iA==", - "dev": true, - "dependencies": { - "@angular-devkit/architect": "0.1502.9", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "webpack": "^5.30.0", - "webpack-dev-server": "^4.0.0" - } - }, - "node_modules/@angular-devkit/core": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.9.tgz", - "integrity": "sha512-6u44YJ9tEG2hiWITL1rwA9yP6ot4a3cyN/UOMRkYSa/XO2Gz5/dM3U74E2kwg+P1NcxLXffBWl0rz8/Y/lSZyQ==", - "dev": true, - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/core/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@angular-devkit/core/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/@angular-devkit/schematics": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.2.9.tgz", - "integrity": "sha512-o08nE8sTpfq/Fknrr1rzBsM8vY36BDox+8dOo9Zc/KqcVPwDy94YKRzHb+xxVaU9jy1VYeCjy63mkyELy7Z3zQ==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "15.2.9", - "jsonc-parser": "3.2.0", - "magic-string": "0.29.0", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/@angular-eslint/builder": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-15.2.1.tgz", - "integrity": "sha512-7x2DANebLRl997Mj4DhZrnz5+vnSjavGGveJ0mBuU7CEsL0ZYLftdRqL0e0HtU3ksseS7xpchD6OM08nkNgySw==", - "dev": true, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-15.2.1.tgz", - "integrity": "sha512-LO7Am8eVCr7oh6a0VmKSL7K03CnQEQhFO7Wt/YtbfYOxVjrbwmYLwJn+wZPOT7A02t/BttOD/WXuDrOWtSMQ/Q==", - "dev": true - }, - "node_modules/@angular-eslint/eslint-plugin": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-15.2.1.tgz", - "integrity": "sha512-OM7b1kS4E4CkXjkaWN+lEzawh4VxY6l7FO1Cuk4s7iv3/YpZG3rJxIZBqnFLTixwrBuqw8y4FNBzF3eDgmFAUw==", - "dev": true, - "dependencies": { - "@angular-eslint/utils": "15.2.1", - "@typescript-eslint/utils": "5.48.2" - }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-15.2.1.tgz", - "integrity": "sha512-IeiSLk6YxapFdH2z5o/O3R7VwtBd2T6fWmhLFPwDYMDknrwegnOjwswCdBplOccpUp0wqlCeGUx7LTsuzwaz7w==", - "dev": true, - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "15.2.1", - "@angular-eslint/utils": "15.2.1", - "@typescript-eslint/type-utils": "5.48.2", - "@typescript-eslint/utils": "5.48.2", - "aria-query": "5.1.3", - "axobject-query": "3.1.1" - }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/template-parser": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-15.2.1.tgz", - "integrity": "sha512-ViCi79gC2aKJecmYLkOT+QlT5WMRNXeYz0Dr9Pr8qXzIbY0oAWE7nOT5jkXwQ9oUk+ybtGCWHma5JVJWVJsIog==", - "dev": true, - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "15.2.1", - "eslint-scope": "^7.0.0" - }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular-eslint/template-parser/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@angular-eslint/template-parser/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/@angular-eslint/utils": { - "version": "15.2.1", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-15.2.1.tgz", - "integrity": "sha512-++FneAJHxJqcSu0igVN6uOkSoHxlzgLoMBswuovYJy3UKwm33/T6WFku8++753Ca/JucIoR1gdUfO7SoSspMDg==", - "dev": true, - "dependencies": { - "@angular-eslint/bundled-angular-compiler": "15.2.1", - "@typescript-eslint/utils": "5.48.2" - }, - "peerDependencies": { - "eslint": "^7.20.0 || ^8.0.0", - "typescript": "*" - } - }, - "node_modules/@angular/animations": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-15.2.9.tgz", - "integrity": "sha512-GQujLhI0cQFcl4Q8y0oSYKSRnW23GIeSL+Arl4eFufziJ9hGAAQNuesaNs/7i+9UlTHDMkPH3kd5ScXuYYz6wg==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "@angular/core": "15.2.9" - } - }, - "node_modules/@angular/cdk": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-15.2.9.tgz", - "integrity": "sha512-koaM07N1AIQ5oHU27l0/FoQSSoYAwlAYwVZ4Di3bYrJsTBNCN2Xsby7wI8gZxdepMnV4Fe9si382BDBov+oO4Q==", - "dependencies": { - "tslib": "^2.3.0" - }, - "optionalDependencies": { - "parse5": "^7.1.2" - }, - "peerDependencies": { - "@angular/common": "^15.0.0 || ^16.0.0", - "@angular/core": "^15.0.0 || ^16.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/cdk/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "optional": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/@angular/cdk/node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "optional": true, - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/@angular/cli": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-15.2.9.tgz", - "integrity": "sha512-mI6hkGyIJDKd8MRiBl3p5chsUhgnluwmpsq3g1FFPw+wv+eXsPYgCiHqXS/OsK+shFxii9XMxoZQO28bJ4NAOQ==", - "dev": true, - "dependencies": { - "@angular-devkit/architect": "0.1502.9", - "@angular-devkit/core": "15.2.9", - "@angular-devkit/schematics": "15.2.9", - "@schematics/angular": "15.2.9", - "@yarnpkg/lockfile": "1.1.0", - "ansi-colors": "4.1.3", - "ini": "3.0.1", - "inquirer": "8.2.4", - "jsonc-parser": "3.2.0", - "npm-package-arg": "10.1.0", - "npm-pick-manifest": "8.0.1", - "open": "8.4.1", - "ora": "5.4.1", - "pacote": "15.1.0", - "resolve": "1.22.1", - "semver": "7.5.3", - "symbol-observable": "4.0.0", - "yargs": "17.6.2" - }, - "bin": { - "ng": "bin/ng.js" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular/cli/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/@angular/cli/node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@angular/common": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-15.2.9.tgz", - "integrity": "sha512-LM9/UHG2dRrOzlu2KovrFwWIziFMjRxHzSP3Igw6Symw/wIl0kXGq8Fn6RpFP78zmLqnv+IQOoRiby9MCXsI4g==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "@angular/core": "15.2.9", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/compiler": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-15.2.9.tgz", - "integrity": "sha512-MoKugbjk+E0wRBj12uvIyDLELlVLonnqjA2+XiF+7FxALIeyds3/qQeEoMmYIqAbN3NnTT5pV92RxWwG4tHFwA==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "@angular/core": "15.2.9" - }, - "peerDependenciesMeta": { - "@angular/core": { - "optional": true - } - } - }, - "node_modules/@angular/compiler-cli": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-15.2.9.tgz", - "integrity": "sha512-zsbI8G2xHOeYWI0hjFzrI//ZhZV9il/uQW5dAimfwJp06KZDeXZ3PdwY9JQslf6F+saLwOObxy6QMrIVvfjy9w==", - "dev": true, - "dependencies": { - "@babel/core": "7.19.3", - "@jridgewell/sourcemap-codec": "^1.4.14", - "chokidar": "^3.0.0", - "convert-source-map": "^1.5.1", - "dependency-graph": "^0.11.0", - "magic-string": "^0.27.0", - "reflect-metadata": "^0.1.2", - "semver": "^7.0.0", - "tslib": "^2.3.0", - "yargs": "^17.2.1" - }, - "bin": { - "ng-xi18n": "bundles/src/bin/ng_xi18n.js", - "ngc": "bundles/src/bin/ngc.js", - "ngcc": "bundles/ngcc/main-ngcc.js" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "@angular/compiler": "15.2.9", - "typescript": ">=4.8.2 <5.0" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.19.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.19.3.tgz", - "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.19.3", - "@babel/helper-compilation-targets": "^7.19.3", - "@babel/helper-module-transforms": "^7.19.0", - "@babel/helpers": "^7.19.0", - "@babel/parser": "^7.19.3", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.19.3", - "@babel/types": "^7.19.3", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@angular/compiler-cli/node_modules/magic-string": { - "version": "0.27.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz", - "integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@angular/core": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-15.2.9.tgz", - "integrity": "sha512-w46Z1yUXCQfKV7XfnamOoLA2VD0MVUUYVrUjO73mHSskDXSXxfZAEHO9kfUS71Cj35PvhP3mbkqWscpea2WeYg==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "rxjs": "^6.5.3 || ^7.4.0", - "zone.js": "~0.11.4 || ~0.12.0 || ~0.13.0" - } - }, - "node_modules/@angular/forms": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-15.2.9.tgz", - "integrity": "sha512-sk0pC2EFi2Ohg5J0q0NYptbT+2WOkoiERSMYA39ncDvlSZBWsNlxpkbGUSck7NIxjK2QfcVN1ldGbHlZTFvtqg==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "@angular/common": "15.2.9", - "@angular/core": "15.2.9", - "@angular/platform-browser": "15.2.9", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/language-service": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-15.2.9.tgz", - "integrity": "sha512-B7lP4q/eHge2lZezOXS96EYzVf4stMCWfOnz7+pUUi0HbF+A5QCV65SWQddS/M+NM2jj8N2L/j+6UCH8lJjTQA==", - "dev": true, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - } - }, - "node_modules/@angular/platform-browser": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-15.2.9.tgz", - "integrity": "sha512-ufCHeSX+U6d43YOMkn3igwfqtlozoCXADcbyfUEG8m2y9XASobqmCKvdSk/zfl62oyiA8msntWBJVBE2l4xKXg==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "@angular/animations": "15.2.9", - "@angular/common": "15.2.9", - "@angular/core": "15.2.9" - }, - "peerDependenciesMeta": { - "@angular/animations": { - "optional": true - } - } - }, - "node_modules/@angular/platform-browser-dynamic": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-15.2.9.tgz", - "integrity": "sha512-ZIYDM6MShblb8OyV1m4+18lJJ2LCeICmeg2uSbpFYptYBSOClrTiYOOFVDJvn7HLvNzljLs16XPrgyaYVqNpcw==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "@angular/common": "15.2.9", - "@angular/compiler": "15.2.9", - "@angular/core": "15.2.9", - "@angular/platform-browser": "15.2.9" - } - }, - "node_modules/@angular/router": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-15.2.9.tgz", - "integrity": "sha512-UCbh5DLSDhybv0xKYT7kGQMfOVdyhHIHOZz5EYVebbhste6S+W1LE57vTHq7QtxJsyKBa/WSkaUkCLXD6ntCAg==", - "dependencies": { - "tslib": "^2.3.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "@angular/common": "15.2.9", - "@angular/core": "15.2.9", - "@angular/platform-browser": "15.2.9", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@angular/service-worker": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-15.2.9.tgz", - "integrity": "sha512-qM/lcrjaxIfpKj174mMWedtGfLNgLl5m7p9mPNODFjqp5lQj3fTTS643ix5Pr0onwbvbNbXu4g67/WXJqap0eA==", - "dependencies": { - "tslib": "^2.3.0" - }, - "bin": { - "ngsw-config": "ngsw-config.js" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0" - }, - "peerDependencies": { - "@angular/common": "15.2.9", - "@angular/core": "15.2.9" - } - }, - "node_modules/@assemblyscript/loader": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.10.1.tgz", - "integrity": "sha512-H71nDOOL8Y7kWRLqf6Sums+01Q5msqBW2KhDUTemh1tvY04eSkSXrK0uj/4mmY0Xr16/3zyZmsrxN7CKuRbNRg==", - "dev": true - }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.20.12", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.20.12.tgz", - "integrity": "sha512-XsMfHovsUYHFMdrIHkZphTN/2Hzzi78R08NuHfDBehym2VsPDL6Zn/JAD/JQdnRvbSsbQc4mVaU1m6JgtTEElg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.7", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-module-transforms": "^7.20.11", - "@babel/helpers": "^7.20.7", - "@babel/parser": "^7.20.7", - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.12", - "@babel/types": "^7.20.7", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.20.14", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.14.tgz", - "integrity": "sha512-AEmuXHdcD3A52HHXxaTmYlb8q/xMEhoRP67B3T4Oq7lbmSoqroMZzjnGj3+i1io3pdnF8iBYVu4Ilj+c4hBxYg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7", - "@jridgewell/gen-mapping": "^0.3.2", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", - "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz", - "integrity": "sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", - "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.23.5", - "@babel/helper-validator-option": "^7.23.5", - "browserslist": "^4.22.2", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.23.7.tgz", - "integrity": "sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-member-expression-to-functions": "^7.23.0", - "@babel/helper-optimise-call-expression": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz", - "integrity": "sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "regexpu-core": "^5.3.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-define-polyfill-provider": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz", - "integrity": "sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==", - "dev": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.17.7", - "@babel/helper-plugin-utils": "^7.16.7", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2", - "semver": "^6.1.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0-0" - } - }, - "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz", - "integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-module-transforms/node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz", - "integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz", - "integrity": "sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-wrap-function": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz", - "integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-member-expression-to-functions": "^7.22.15", - "@babel/helper-optimise-call-expression": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz", - "integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.22.20.tgz", - "integrity": "sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==", - "dev": true, - "dependencies": { - "@babel/helper-function-name": "^7.22.5", - "@babel/template": "^7.22.15", - "@babel/types": "^7.22.19" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz", - "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.7", - "@babel/types": "^7.23.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz", - "integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.23.3.tgz", - "integrity": "sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.23.3.tgz", - "integrity": "sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-transform-optional-chaining": "^7.23.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-proposal-async-generator-functions": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz", - "integrity": "sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-async-generator-functions instead.", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9", - "@babel/plugin-syntax-async-generators": "^7.8.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-class-static-block": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz", - "integrity": "sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-static-block instead.", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-class-static-block": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-proposal-dynamic-import": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", - "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-dynamic-import instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-dynamic-import": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-export-namespace-from": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", - "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-export-namespace-from instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.9", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-json-strings": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", - "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-json-strings instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-json-strings": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz", - "integrity": "sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-logical-assignment-operators instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", - "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-numeric-separator": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", - "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-numeric-separator instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-catch-binding": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", - "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-catch-binding instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.18.6", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-optional-chaining": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz", - "integrity": "sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead.", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-skip-transparent-expression-wrappers": "^7.20.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-methods": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", - "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead.", - "dev": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-private-property-in-object": { - "version": "7.21.11", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz", - "integrity": "sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead.", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.18.6", - "@babel/helper-create-class-features-plugin": "^7.21.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-unicode-property-regex": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", - "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-unicode-property-regex instead.", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.23.3.tgz", - "integrity": "sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.23.3.tgz", - "integrity": "sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz", - "integrity": "sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-remap-async-to-generator": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.23.3.tgz", - "integrity": "sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.23.4.tgz", - "integrity": "sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.23.8.tgz", - "integrity": "sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-compilation-targets": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20", - "@babel/helper-split-export-declaration": "^7.22.6", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", - "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.23.3.tgz", - "integrity": "sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/template": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties/node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.23.3.tgz", - "integrity": "sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.23.3.tgz", - "integrity": "sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.23.3.tgz", - "integrity": "sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.23.3.tgz", - "integrity": "sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==", - "dev": true, - "dependencies": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.23.6.tgz", - "integrity": "sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.23.3.tgz", - "integrity": "sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==", - "dev": true, - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.23.3.tgz", - "integrity": "sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.23.3.tgz", - "integrity": "sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.23.3.tgz", - "integrity": "sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.3.tgz", - "integrity": "sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.23.3.tgz", - "integrity": "sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==", - "dev": true, - "dependencies": { - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.23.3.tgz", - "integrity": "sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==", - "dev": true, - "dependencies": { - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz", - "integrity": "sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.5", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.23.3.tgz", - "integrity": "sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.23.3.tgz", - "integrity": "sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-replace-supers": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.23.4.tgz", - "integrity": "sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz", - "integrity": "sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.23.3.tgz", - "integrity": "sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz", - "integrity": "sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "regenerator-transform": "^0.15.2" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.23.3.tgz", - "integrity": "sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime": { - "version": "7.19.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.19.6.tgz", - "integrity": "sha512-PRH37lz4JU156lYFW1p8OxE5i7d6Sl/zV58ooyr+q1J1lnQPyg5tIiXlIwNVhJaY4W3TmOtdc8jqdXQcB1v5Yw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-plugin-utils": "^7.19.0", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.23.3.tgz", - "integrity": "sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.23.3.tgz", - "integrity": "sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.23.3.tgz", - "integrity": "sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.23.3.tgz", - "integrity": "sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.23.3.tgz", - "integrity": "sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.23.3.tgz", - "integrity": "sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.23.3.tgz", - "integrity": "sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==", - "dev": true, - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.22.15", - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.20.2.tgz", - "integrity": "sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.20.1", - "@babel/helper-compilation-targets": "^7.20.0", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/helper-validator-option": "^7.18.6", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-async-generator-functions": "^7.20.1", - "@babel/plugin-proposal-class-properties": "^7.18.6", - "@babel/plugin-proposal-class-static-block": "^7.18.6", - "@babel/plugin-proposal-dynamic-import": "^7.18.6", - "@babel/plugin-proposal-export-namespace-from": "^7.18.9", - "@babel/plugin-proposal-json-strings": "^7.18.6", - "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", - "@babel/plugin-proposal-numeric-separator": "^7.18.6", - "@babel/plugin-proposal-object-rest-spread": "^7.20.2", - "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", - "@babel/plugin-proposal-optional-chaining": "^7.18.9", - "@babel/plugin-proposal-private-methods": "^7.18.6", - "@babel/plugin-proposal-private-property-in-object": "^7.18.6", - "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-dynamic-import": "^7.8.3", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-import-assertions": "^7.20.0", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5", - "@babel/plugin-transform-arrow-functions": "^7.18.6", - "@babel/plugin-transform-async-to-generator": "^7.18.6", - "@babel/plugin-transform-block-scoped-functions": "^7.18.6", - "@babel/plugin-transform-block-scoping": "^7.20.2", - "@babel/plugin-transform-classes": "^7.20.2", - "@babel/plugin-transform-computed-properties": "^7.18.9", - "@babel/plugin-transform-destructuring": "^7.20.2", - "@babel/plugin-transform-dotall-regex": "^7.18.6", - "@babel/plugin-transform-duplicate-keys": "^7.18.9", - "@babel/plugin-transform-exponentiation-operator": "^7.18.6", - "@babel/plugin-transform-for-of": "^7.18.8", - "@babel/plugin-transform-function-name": "^7.18.9", - "@babel/plugin-transform-literals": "^7.18.9", - "@babel/plugin-transform-member-expression-literals": "^7.18.6", - "@babel/plugin-transform-modules-amd": "^7.19.6", - "@babel/plugin-transform-modules-commonjs": "^7.19.6", - "@babel/plugin-transform-modules-systemjs": "^7.19.6", - "@babel/plugin-transform-modules-umd": "^7.18.6", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.19.1", - "@babel/plugin-transform-new-target": "^7.18.6", - "@babel/plugin-transform-object-super": "^7.18.6", - "@babel/plugin-transform-parameters": "^7.20.1", - "@babel/plugin-transform-property-literals": "^7.18.6", - "@babel/plugin-transform-regenerator": "^7.18.6", - "@babel/plugin-transform-reserved-words": "^7.18.6", - "@babel/plugin-transform-shorthand-properties": "^7.18.6", - "@babel/plugin-transform-spread": "^7.19.0", - "@babel/plugin-transform-sticky-regex": "^7.18.6", - "@babel/plugin-transform-template-literals": "^7.18.9", - "@babel/plugin-transform-typeof-symbol": "^7.18.9", - "@babel/plugin-transform-unicode-escapes": "^7.18.10", - "@babel/plugin-transform-unicode-regex": "^7.18.6", - "@babel/preset-modules": "^0.1.5", - "@babel/types": "^7.20.2", - "babel-plugin-polyfill-corejs2": "^0.3.3", - "babel-plugin-polyfill-corejs3": "^0.6.0", - "babel-plugin-polyfill-regenerator": "^0.4.1", - "core-js-compat": "^3.25.1", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/preset-modules": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6.tgz", - "integrity": "sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/regjsgen": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz", - "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==", - "dev": true - }, - "node_modules/@babel/runtime": { - "version": "7.20.13", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", - "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", - "integrity": "sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz", - "integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.6", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/generator": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", - "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.6", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.23.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz", - "integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@es-joy/jsdoccomment": { - "version": "0.39.4", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz", - "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==", - "dev": true, - "dependencies": { - "comment-parser": "1.3.1", - "esquery": "^1.5.0", - "jsdoc-type-pratt-parser": "~4.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.8.tgz", - "integrity": "sha512-ghAbV3ia2zybEefXRRm7+lx8J/rnupZT0gp9CaGy/3iolEXkJ6LYRq4IpQVI9zR97ID80KJVoUlo3LSeA/sMAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", - "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "dev": true - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", - "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", - "dev": true - }, - "node_modules/@ionic/angular": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@ionic/angular/-/angular-6.7.4.tgz", - "integrity": "sha512-E41OEAXZFe7rhtseD0R+cEsE2qOxMBGeCGwNNdnoiWtU35+I60YM0M1NaXUd8iiSCUX6v794CETQQeazBxSORg==", - "dependencies": { - "@ionic/core": "6.7.4", - "ionicons": "^6.1.3", - "jsonc-parser": "^3.0.0", - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/core": ">=12.0.0", - "@angular/forms": ">=12.0.0", - "@angular/router": ">=12.0.0", - "rxjs": ">=6.6.0", - "zone.js": ">=0.11.0" - } - }, - "node_modules/@ionic/angular-toolkit": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@ionic/angular-toolkit/-/angular-toolkit-7.0.0.tgz", - "integrity": "sha512-9nVeGuGRO3sUYSVzcHmddPd9+C+XDW2BpRSxCqMK1MKLlfnnpVMP1TTf/IDQhBj+koHwBAQBj6voBSsFUzTKBg==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "^14.0.0", - "@angular-devkit/schematics": "^14.0.0", - "@schematics/angular": "^14.0.0" - } - }, - "node_modules/@ionic/angular-toolkit/node_modules/@angular-devkit/core": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.13.tgz", - "integrity": "sha512-aIefeZcbjghQg/V6U9CTLtyB5fXDJ63KwYqVYkWP+i0XriS5A9puFgq2u/OVsWxAfYvqpDqp5AdQ0g0bi3CAsA==", - "dev": true, - "dependencies": { - "ajv": "8.11.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.1.0", - "rxjs": "6.6.7", - "source-map": "0.7.4" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@ionic/angular-toolkit/node_modules/@angular-devkit/schematics": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.13.tgz", - "integrity": "sha512-2zczyeNzeBcrT2HOysv52X9SH3tZoHfWJvVf6H0SIa74rfDKEl7hFpKNXnh3x8sIMLj5mZn05n5RCqGxCczcIg==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "14.2.13", - "jsonc-parser": "3.1.0", - "magic-string": "0.26.2", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@ionic/angular-toolkit/node_modules/@schematics/angular": { - "version": "14.2.13", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.2.13.tgz", - "integrity": "sha512-MLxTpTU3E8QACQ/5c0sENMR2gRiMXpGaKeD5IHY+3wyU2fUSJVB0QPU/l1WhoyZbX8N9ospBgf5UEG7taVF9rg==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "14.2.13", - "@angular-devkit/schematics": "14.2.13", - "jsonc-parser": "3.1.0" - }, - "engines": { - "node": "^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@ionic/angular-toolkit/node_modules/magic-string": { - "version": "0.26.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", - "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", - "dev": true, - "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@ionic/core": { - "version": "6.7.4", - "resolved": "https://registry.npmjs.org/@ionic/core/-/core-6.7.4.tgz", - "integrity": "sha512-IG6vQtt4xrJdas6k1CwqahD/BWsYK6Gi/BAIN8TumBmtfNMu38iOG6Dh05q4hCQzmDm2xDS/BVD3Qz7AmOKArA==", - "dependencies": { - "@stencil/core": "^2.18.0", - "ionicons": "^6.1.3", - "tslib": "^2.1.0" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/@isaacs/cliui/node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@isaacs/cliui/node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@kurkle/color": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", - "dev": true - }, - "node_modules/@ngtools/webpack": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-15.2.9.tgz", - "integrity": "sha512-nOXUGqKkAEMlCcrhkDwWDzcVdKNH7MNRUXfNzsFc9zdeR/5p3qt6SVMN7OOE3NREyI7P6nzARc3S+6QDBjf3Jg==", - "dev": true, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "@angular/compiler-cli": "^15.0.0", - "typescript": ">=4.8.2 <5.0", - "webpack": "^5.54.0" - } - }, - "node_modules/@ngx-formly/core": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/@ngx-formly/core/-/core-6.1.7.tgz", - "integrity": "sha512-hFale+MISRuRznbi4Q7HQaXDICl6wtMneJcnQGmYv8yjeaOYjBsogpD4cOeDf3GibrHYk7Ggh4mamGaLdCL65A==", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/forms": ">=13.2.0", - "rxjs": "^6.5.3 || ^7.0.0" - } - }, - "node_modules/@ngx-formly/ionic": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/@ngx-formly/ionic/-/ionic-6.1.7.tgz", - "integrity": "sha512-T1rXjM2L8LvbtE2xdeyStECHjipy7ZqmixI7uVJ/2+7i4frD30UisAW25+Kg1E3Bq2SpQ+De+tEg/B1kOGclWA==", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@ionic/angular": "^6.0.0", - "@ngx-formly/core": "6.1.7" - } - }, - "node_modules/@ngx-formly/schematics": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/@ngx-formly/schematics/-/schematics-6.1.7.tgz", - "integrity": "sha512-My7Caz9zNS8NQKtAQTF21YIZnJZ9KZTlh1GPjydlYRpLp2zkfsEiu7nUDBFZV6e0/pLLnlPuaeEkXEuK8SPCZw==", - "dependencies": { - "@angular-devkit/core": "^13.0.3", - "@angular-devkit/schematics": "^13.0.3", - "@schematics/angular": "^13.0.3" - } - }, - "node_modules/@ngx-formly/schematics/node_modules/@angular-devkit/core": { - "version": "13.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-13.3.11.tgz", - "integrity": "sha512-rfqoLMRYhlz0wzKlHx7FfyIyQq8dKTsmbCoIVU1cEIH0gyTMVY7PbVzwRRcO6xp5waY+0hA+0Brriujpuhkm4w==", - "dependencies": { - "ajv": "8.9.0", - "ajv-formats": "2.1.1", - "fast-json-stable-stringify": "2.1.0", - "magic-string": "0.25.7", - "rxjs": "6.6.7", - "source-map": "0.7.3" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@ngx-formly/schematics/node_modules/@angular-devkit/schematics": { - "version": "13.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-13.3.11.tgz", - "integrity": "sha512-ben+EGXpCrClnIVAAnEQmhQdKmnnqFhMp5BqMxgOslSYBAmCutLA6rBu5vsc8kZcGian1wt+lueF7G1Uk5cGBg==", - "dependencies": { - "@angular-devkit/core": "13.3.11", - "jsonc-parser": "3.0.0", - "magic-string": "0.25.7", - "ora": "5.4.1", - "rxjs": "6.6.7" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@ngx-formly/schematics/node_modules/@schematics/angular": { - "version": "13.3.11", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-13.3.11.tgz", - "integrity": "sha512-imKBnKYEse0SBVELZO/753nkpt3eEgpjrYkB+AFWF9YfO/4RGnYXDHoH8CFkzxPH9QQCgNrmsVFNiYGS+P/S1A==", - "dependencies": { - "@angular-devkit/core": "13.3.11", - "@angular-devkit/schematics": "13.3.11", - "jsonc-parser": "3.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.15.0 || >=16.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@ngx-formly/schematics/node_modules/ajv": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.9.0.tgz", - "integrity": "sha512-qOKJyNj/h+OWx7s5DePL6Zu1KeM9jPZhwBqs+7DzP6bGOvqzVCSf0xueYmVuaC/oQ/VtS2zLMLHdQFbkka+XDQ==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@ngx-formly/schematics/node_modules/jsonc-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.0.0.tgz", - "integrity": "sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==" - }, - "node_modules/@ngx-formly/schematics/node_modules/magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dependencies": { - "sourcemap-codec": "^1.4.4" - } - }, - "node_modules/@ngx-formly/schematics/node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@ngx-translate/core": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-14.0.0.tgz", - "integrity": "sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/core": ">=13.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@npmcli/fs": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", - "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", - "dev": true, - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/git": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.1.0.tgz", - "integrity": "sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ==", - "dev": true, - "dependencies": { - "@npmcli/promise-spawn": "^6.0.0", - "lru-cache": "^7.4.4", - "npm-pick-manifest": "^8.0.0", - "proc-log": "^3.0.0", - "promise-inflight": "^1.0.1", - "promise-retry": "^2.0.1", - "semver": "^7.3.5", - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/git/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@npmcli/git/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/installed-package-contents": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz", - "integrity": "sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==", - "dev": true, - "dependencies": { - "npm-bundled": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "bin": { - "installed-package-contents": "lib/index.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/move-file": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", - "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "dev": true, - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/@npmcli/node-gyp": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", - "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/promise-spawn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", - "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", - "dev": true, - "dependencies": { - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/promise-spawn/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.2.tgz", - "integrity": "sha512-NCcr1uQo1k5U+SYlnIrbAh3cxy+OQT1VtqiAbxdymSlptbzBb62AjH2xXgjNCoP073hoa1CfCAcwoZ8k96C4nA==", - "dev": true, - "dependencies": { - "@npmcli/node-gyp": "^3.0.0", - "@npmcli/promise-spawn": "^6.0.0", - "node-gyp": "^9.0.0", - "read-package-json-fast": "^3.0.0", - "which": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@npmcli/run-script/node_modules/which": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-3.0.1.tgz", - "integrity": "sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/which.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@schematics/angular": { - "version": "15.2.9", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-15.2.9.tgz", - "integrity": "sha512-0Lit6TLNUwcAYiEkXgZp3vY9xAO1cnZCBXuUcp+6v+Ddnrt2w/YOiGe74p21cYe0StkTpTljsqsKBTiX7TMjQg==", - "dev": true, - "dependencies": { - "@angular-devkit/core": "15.2.9", - "@angular-devkit/schematics": "15.2.9", - "jsonc-parser": "3.2.0" - }, - "engines": { - "node": "^14.20.0 || ^16.13.0 || >=18.10.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@schematics/angular/node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "node_modules/@sigstore/bundle": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-1.1.0.tgz", - "integrity": "sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==", - "dev": true, - "dependencies": { - "@sigstore/protobuf-specs": "^0.2.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/protobuf-specs": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz", - "integrity": "sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-1.0.0.tgz", - "integrity": "sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==", - "dev": true, - "dependencies": { - "@sigstore/bundle": "^1.1.0", - "@sigstore/protobuf-specs": "^0.2.0", - "make-fetch-happen": "^11.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/@sigstore/sign/node_modules/make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@sigstore/sign/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sigstore/sign/node_modules/minipass-fetch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", - "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/@sigstore/sign/node_modules/minipass-fetch/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@sigstore/tuf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.3.tgz", - "integrity": "sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==", - "dev": true, - "dependencies": { - "@sigstore/protobuf-specs": "^0.2.0", - "tuf-js": "^1.1.7" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", - "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", - "dev": true - }, - "node_modules/@stencil/core": { - "version": "2.22.3", - "resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.22.3.tgz", - "integrity": "sha512-kmVA0M/HojwsfkeHsifvHVIYe4l5tin7J5+DLgtl8h6WWfiMClND5K3ifCXXI2ETDNKiEk21p6jql3Fx9o2rng==", - "bin": { - "stencil": "bin/stencil" - }, - "engines": { - "node": ">=12.10.0", - "npm": ">=6.0.0" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", - "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@tufjs/canonical-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-1.0.0.tgz", - "integrity": "sha512-QTnf++uxunWvG2z3UFNzAoQPHxnSXOwtaI3iJ+AohhV+5vONuArPjJE7aPXPVXfXJsqrVbZBu9b81AJoSd09IQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@tufjs/models": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.4.tgz", - "integrity": "sha512-qaGV9ltJP0EO25YfFUPhxRVK0evXFIAGicsVXuRim4Ed9cjPxYhNnNJ49SFmbeLgtxpslIkX317IgpfcHPVj/A==", - "dev": true, - "dependencies": { - "@tufjs/canonical-json": "1.0.0", - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@tufjs/models/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "dev": true, - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", - "dev": true - }, - "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "8.56.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.2.tgz", - "integrity": "sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==", - "dev": true, - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true - }, - "node_modules/@types/express": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", - "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", - "dev": true, - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.17.41", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", - "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", - "dev": true, - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true - }, - "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/jasmine": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-4.3.2.tgz", - "integrity": "sha512-lKkWBcbxEZX/7nxPqEtv/OjPLaBa2j0o+hmv5Yn83b/+11C1kfBAkgvmrb13WDkmizUJ3B+jYrWh4M0YRtrzEQ==", - "dev": true - }, - "node_modules/@types/jasminewd2": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.10.tgz", - "integrity": "sha512-J7mDz7ovjwjc+Y9rR9rY53hFWKATcIkrr9DwQWmOas4/pnIPJTXawnzjwpHm3RSxz/e3ZVUvQ7cRbd5UQLo10g==", - "dev": true, - "dependencies": { - "@types/jasmine": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.2.5.tgz", - "integrity": "sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==", - "dev": true - }, - "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/parse-json": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", - "dev": true - }, - "node_modules/@types/q": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", - "integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==", - "dev": true - }, - "node_modules/@types/qs": { - "version": "6.9.11", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", - "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", - "dev": true - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true - }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true - }, - "node_modules/@types/selenium-webdriver": { - "version": "3.0.26", - "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.26.tgz", - "integrity": "sha512-dyIGFKXfUFiwkMfNGn1+F6b80ZjR3uSYv1j6xVJSDlft5waZ2cwkHW4e7zNzvq7hiEackcgvBpmnXZrI1GltPg==", - "dev": true - }, - "node_modules/@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", - "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", - "dev": true, - "dependencies": { - "@types/http-errors": "*", - "@types/mime": "*", - "@types/node": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/uuid": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.2.tgz", - "integrity": "sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==", - "dev": true - }, - "node_modules/@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.2.0.tgz", - "integrity": "sha512-rClGrMuyS/3j0ETa1Ui7s6GkLhfZGKZL3ZrChLeAiACBE/tRc1wq8SNZESUuluxhLj9FkUefRs2l6bCIArWBiQ==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.2.0", - "@typescript-eslint/type-utils": "6.2.0", - "@typescript-eslint/utils": "6.2.0", - "@typescript-eslint/visitor-keys": "6.2.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.2.0.tgz", - "integrity": "sha512-1ZMNVgm5nnHURU8ZSJ3snsHzpFeNK84rdZjluEVBGNu7jDymfqceB3kdIZ6A4xCfEFFhRIB6rF8q/JIqJd2R0Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.2.0", - "@typescript-eslint/visitor-keys": "6.2.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.2.0.tgz", - "integrity": "sha512-DnGZuNU2JN3AYwddYIqrVkYW0uUQdv0AY+kz2M25euVNlujcN2u+rJgfJsBFlUEzBB6OQkUqSZPyuTLf2bP5mw==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.2.0", - "@typescript-eslint/utils": "6.2.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.2.0.tgz", - "integrity": "sha512-1nRRaDlp/XYJQLvkQJG5F3uBTno5SHPT7XVcJ5n1/k2WfNI28nJsvLakxwZRNY5spuatEKO7d5nZWsQpkqXwBA==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.2.0.tgz", - "integrity": "sha512-Mts6+3HQMSM+LZCglsc2yMIny37IhUgp1Qe8yJUYVyO6rHP7/vN0vajKu3JvHCBIy8TSiKddJ/Zwu80jhnGj1w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.2.0", - "@typescript-eslint/visitor-keys": "6.2.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.2.0.tgz", - "integrity": "sha512-RCFrC1lXiX1qEZN8LmLrxYRhOkElEsPKTVSNout8DMzf8PeWoQG7Rxz2SadpJa3VSh5oYKGwt7j7X/VRg+Y3OQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.2.0", - "@typescript-eslint/types": "6.2.0", - "@typescript-eslint/typescript-estree": "6.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.2.0.tgz", - "integrity": "sha512-QbaYUQVKKo9bgCzpjz45llCfwakyoxHetIy8CAvYCtd16Zu1KrpzNHofwF8kGkpPOxZB2o6kz+0nqH8ZkIzuoQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.2.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.2.0.tgz", - "integrity": "sha512-igVYOqtiK/UsvKAmmloQAruAdUHihsOCvplJpplPZ+3h4aDkC/UKZZNKgB6h93ayuYLuEymU3h8nF1xMRbh37g==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.2.0", - "@typescript-eslint/types": "6.2.0", - "@typescript-eslint/typescript-estree": "6.2.0", - "@typescript-eslint/visitor-keys": "6.2.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.2.0.tgz", - "integrity": "sha512-1ZMNVgm5nnHURU8ZSJ3snsHzpFeNK84rdZjluEVBGNu7jDymfqceB3kdIZ6A4xCfEFFhRIB6rF8q/JIqJd2R0Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.2.0", - "@typescript-eslint/visitor-keys": "6.2.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.2.0.tgz", - "integrity": "sha512-1nRRaDlp/XYJQLvkQJG5F3uBTno5SHPT7XVcJ5n1/k2WfNI28nJsvLakxwZRNY5spuatEKO7d5nZWsQpkqXwBA==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.2.0.tgz", - "integrity": "sha512-Mts6+3HQMSM+LZCglsc2yMIny37IhUgp1Qe8yJUYVyO6rHP7/vN0vajKu3JvHCBIy8TSiKddJ/Zwu80jhnGj1w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.2.0", - "@typescript-eslint/visitor-keys": "6.2.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.2.0.tgz", - "integrity": "sha512-QbaYUQVKKo9bgCzpjz45llCfwakyoxHetIy8CAvYCtd16Zu1KrpzNHofwF8kGkpPOxZB2o6kz+0nqH8ZkIzuoQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.2.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/parser/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz", - "integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/visitor-keys": "5.48.2" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.48.2.tgz", - "integrity": "sha512-QVWx7J5sPMRiOMJp5dYshPxABRoZV1xbRirqSk8yuIIsu0nvMTZesKErEA3Oix1k+uvsk8Cs8TGJ6kQ0ndAcew==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "5.48.2", - "@typescript-eslint/utils": "5.48.2", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz", - "integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz", - "integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/visitor-keys": "5.48.2", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz", - "integrity": "sha512-2h18c0d7jgkw6tdKTlNaM7wyopbLRBiit8oAxoP89YnuBOzCZ8g8aBCaCqq7h208qUTroL7Whgzam7UY3HVLow==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.48.2", - "@typescript-eslint/types": "5.48.2", - "@typescript-eslint/typescript-estree": "5.48.2", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.48.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz", - "integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.48.2", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", - "dev": true, - "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", - "dev": true, - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", - "dev": true, - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", - "dev": true, - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", - "dev": true, - "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true - }, - "node_modules/@yarnpkg/lockfile": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", - "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", - "dev": true - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", - "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/adjust-sourcemap-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz", - "integrity": "sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==", - "dev": true, - "dependencies": { - "loader-utils": "^2.0.0", - "regex-parser": "^2.2.11" - }, - "engines": { - "node": ">=8.9" - } - }, - "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/adm-zip": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz", - "integrity": "sha512-x0HvcHqVJNTPk/Bw8JbLWlWoo6Wwnsug0fnYYro1HBrjxZ3G7/AZk7Ahv8JwDe1uIcz8eBqvu86FuF1POiG7vQ==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "dev": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/angular-mydatepicker": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/angular-mydatepicker/-/angular-mydatepicker-0.11.5.tgz", - "integrity": "sha512-nKXUA3NhzMJ02jX+AiqpEr0kl2lCS3tGaFeHcyBFhwlDao9BO9i3403NXaFliOCeN2M4VvUNQs8pggLE7DECIA==", - "dependencies": { - "tslib": "^1.9.0" - } - }, - "node_modules/angular-mydatepicker/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "devOptional": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "dev": true - }, - "node_modules/are-docs-informative": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", - "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "dev": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/autoprefixer": { - "version": "10.4.13", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", - "integrity": "sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - } - ], - "dependencies": { - "browserslist": "^4.21.4", - "caniuse-lite": "^1.0.30001426", - "fraction.js": "^4.2.0", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==", - "dev": true - }, - "node_modules/axobject-query": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", - "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", - "dev": true, - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/babel-loader": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.1.2.tgz", - "integrity": "sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA==", - "dev": true, - "dependencies": { - "find-cache-dir": "^3.3.2", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz", - "integrity": "sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.17.7", - "@babel/helper-define-polyfill-provider": "^0.3.3", - "semver": "^6.1.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz", - "integrity": "sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3", - "core-js-compat": "^3.25.1" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz", - "integrity": "sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==", - "dev": true, - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.3.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "dev": true, - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "devOptional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/blocking-proxy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz", - "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "blocking-proxy": "built/lib/bin.js" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/body-parser": { - "version": "1.20.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", - "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/bonjour-service": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", - "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "devOptional": true, - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.21.5", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz", - "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001449", - "electron-to-chromium": "^1.4.284", - "node-releases": "^2.0.8", - "update-browserslist-db": "^1.0.10" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/browserstack": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz", - "integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==", - "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" - } - }, - "node_modules/browserstack/node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/browserstack/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/browserstack/node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "dependencies": { - "semver": "^7.0.0" - } - }, - "node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacache": { - "version": "17.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.4.tgz", - "integrity": "sha512-Z/nL3gU+zTUjz5pCA5vVjYM8pmaw2kxM7JEiE0fv3w77Wj+sFbi70CrBruUWH0uNcEdvLDixFpgA2JM4F4DBjA==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^3.1.0", - "fs-minipass": "^3.0.0", - "glob": "^8.0.1", - "lru-cache": "^7.7.1", - "minipass": "^4.0.0", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001576", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", - "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", - "dev": true - }, - "node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "node_modules/chart.js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz", - "integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==", - "dependencies": { - "@kurkle/color": "^0.3.0" - }, - "engines": { - "pnpm": ">=7" - } - }, - "node_modules/chartjs-adapter-date-fns": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chartjs-adapter-date-fns/-/chartjs-adapter-date-fns-3.0.0.tgz", - "integrity": "sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==", - "peerDependencies": { - "chart.js": ">=2.8.0", - "date-fns": ">=2.0.0" - } - }, - "node_modules/chartjs-plugin-zoom": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/chartjs-plugin-zoom/-/chartjs-plugin-zoom-2.0.1.tgz", - "integrity": "sha512-ogOmLu6e+Q7E1XWOCOz9YwybMslz9qNfGV2a+qjfmqJYpsw5ZMoRHZBUyW+NGhkpQ5PwwPA/+rikHpBZb7PZuA==", - "dependencies": { - "hammerjs": "^2.0.8" - }, - "peerDependencies": { - "chart.js": ">=3.2.0" - } - }, - "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "engines": { - "node": ">=6.0" - } - }, - "node_modules/classlist.js": { - "version": "1.1.20150312", - "resolved": "https://registry.npmjs.org/classlist.js/-/classlist.js-1.1.20150312.tgz", - "integrity": "sha512-eR8yB970+yGslcTnJnROX2icsMa8v/KVLv/sgv3NhSvZSHgam64XNSF2TyJnKIfsnTFJBcTdrIneYqUIrvxLpg==" - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color-convert/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "dev": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true - }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "engines": { - "node": ">= 10" - } - }, - "node_modules/comment-parser": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz", - "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==", - "dev": true, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", - "dev": true - }, - "node_modules/compare-versions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz", - "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", - "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", - "dev": true, - "dependencies": { - "accepts": "~1.3.5", - "bytes": "3.0.0", - "compressible": "~2.0.16", - "debug": "2.6.9", - "on-headers": "~1.0.2", - "safe-buffer": "5.1.2", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/connect": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", - "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "finalhandler": "1.1.2", - "parseurl": "~1.3.3", - "utils-merge": "1.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "dev": true, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/connect/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/connect/node_modules/finalhandler": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", - "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "~2.3.0", - "parseurl": "~1.3.3", - "statuses": "~1.5.0", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/connect/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/connect/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/connect/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "dev": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true - }, - "node_modules/copy-anything": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz", - "integrity": "sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==", - "dev": true, - "dependencies": { - "is-what": "^3.14.1" - }, - "funding": { - "url": "https://github.com/sponsors/mesqueeb" - } - }, - "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", - "dev": true, - "dependencies": { - "fast-glob": "^3.2.11", - "glob-parent": "^6.0.1", - "globby": "^13.1.1", - "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, - "node_modules/copy-webpack-plugin/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/core-js-compat": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.35.0.tgz", - "integrity": "sha512-5blwFAddknKeNgsjBzilkdQ0+YK8L1PfqPYq40NOYMYFSS38qj+hpTcLLWwpIwA2A5bje/x5jmVn2tzUMg9IVw==", - "dev": true, - "dependencies": { - "browserslist": "^4.22.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-compat/node_modules/browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "dev": true, - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", - "dev": true, - "dependencies": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.2.1", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.10.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/critters": { - "version": "0.0.16", - "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", - "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "css-select": "^4.2.0", - "parse5": "^6.0.1", - "parse5-htmlparser2-tree-adapter": "^6.0.1", - "postcss": "^8.3.7", - "pretty-bytes": "^5.3.0" - } - }, - "node_modules/critters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/critters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/critters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/critters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/critters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-loader": { - "version": "6.7.3", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.3.tgz", - "integrity": "sha512-qhOH1KlBMnZP8FzRO6YCH9UHXQhVMcEGLyNdb7Hv2cpcmJbW0YrddO+tG1ab5nT41KpHIYGsbeHqxB9xPu1pKQ==", - "dev": true, - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.19", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.0", - "postcss-modules-scope": "^3.0.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/custom-event": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", - "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==", - "dev": true - }, - "node_modules/d3": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", - "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", - "dependencies": { - "d3-array": "3", - "d3-axis": "3", - "d3-brush": "3", - "d3-chord": "3", - "d3-color": "3", - "d3-contour": "4", - "d3-delaunay": "6", - "d3-dispatch": "3", - "d3-drag": "3", - "d3-dsv": "3", - "d3-ease": "3", - "d3-fetch": "3", - "d3-force": "3", - "d3-format": "3", - "d3-geo": "3", - "d3-hierarchy": "3", - "d3-interpolate": "3", - "d3-path": "3", - "d3-polygon": "3", - "d3-quadtree": "3", - "d3-random": "3", - "d3-scale": "4", - "d3-scale-chromatic": "3", - "d3-selection": "3", - "d3-shape": "3", - "d3-time": "3", - "d3-time-format": "4", - "d3-timer": "3", - "d3-transition": "3", - "d3-zoom": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-axis": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", - "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-brush": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", - "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "3", - "d3-transition": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-chord": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", - "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", - "dependencies": { - "d3-path": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-contour": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", - "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", - "dependencies": { - "d3-array": "^3.2.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-delaunay": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", - "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", - "dependencies": { - "delaunator": "5" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dispatch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", - "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-drag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", - "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-selection": "3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-dsv": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", - "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", - "dependencies": { - "commander": "7", - "iconv-lite": "0.6", - "rw": "1" - }, - "bin": { - "csv2json": "bin/dsv2json.js", - "csv2tsv": "bin/dsv2dsv.js", - "dsv2dsv": "bin/dsv2dsv.js", - "dsv2json": "bin/dsv2json.js", - "json2csv": "bin/json2dsv.js", - "json2dsv": "bin/json2dsv.js", - "json2tsv": "bin/json2dsv.js", - "tsv2csv": "bin/dsv2dsv.js", - "tsv2json": "bin/dsv2json.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-fetch": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", - "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", - "dependencies": { - "d3-dsv": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-force": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", - "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-quadtree": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-geo": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", - "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", - "dependencies": { - "d3-array": "2.5.0 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-hierarchy": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", - "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-polygon": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", - "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-quadtree": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", - "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-random": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", - "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale-chromatic": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", - "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", - "dependencies": { - "d3-color": "1 - 3", - "d3-interpolate": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-selection": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", - "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-transition": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", - "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", - "dependencies": { - "d3-color": "1 - 3", - "d3-dispatch": "1 - 3", - "d3-ease": "1 - 3", - "d3-interpolate": "1 - 3", - "d3-timer": "1 - 3" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "d3-selection": "2 - 3" - } - }, - "node_modules/d3-zoom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", - "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", - "dependencies": { - "d3-dispatch": "1 - 3", - "d3-drag": "2 - 3", - "d3-interpolate": "1 - 3", - "d3-selection": "2 - 3", - "d3-transition": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/date-fns": { - "version": "2.30.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "node_modules/date-fns/node_modules/@babel/runtime": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", - "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/date-fns/node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" - }, - "node_modules/date-format": { - "version": "4.0.14", - "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", - "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-equal/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/default-gateway": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", - "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", - "dev": true, - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha512-Z4fzpbIRjOu7lO5jCETSWoqUDVe0IPOlfugBsF6suen2LKDlVb4QZpKEM9P+buNJ4KI1eN7I083w/pbKUpsrWQ==", - "dev": true, - "dependencies": { - "globby": "^5.0.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "rimraf": "^2.2.8" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==", - "dev": true, - "dependencies": { - "array-uniq": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/del/node_modules/globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==", - "dev": true, - "dependencies": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/del/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/delaunator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", - "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", - "dependencies": { - "robust-predicates": "^3.0.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true - }, - "node_modules/di": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz", - "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==", - "dev": true - }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "dev": true, - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dom-serialize": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz", - "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==", - "dev": true, - "dependencies": { - "custom-event": "~1.0.0", - "ent": "~2.2.0", - "extend": "^3.0.0", - "void-elements": "^2.0.0" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.4.628", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.628.tgz", - "integrity": "sha512-2k7t5PHvLsufpP6Zwk0nof62yLOsCf032wZx7/q0mv8gwlXjhcxI3lz6f0jBr0GrnWKcm3burXzI3t5IrcdUxw==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "dev": true, - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/engine.io": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", - "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", - "dev": true, - "dependencies": { - "@types/cookie": "^0.4.1", - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.4.1", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.11.0" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", - "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/cookie": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", - "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/ent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", - "integrity": "sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==", - "dev": true - }, - "node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "dev": true - }, - "node_modules/errno": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", - "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==", - "dev": true, - "optional": true, - "dependencies": { - "prr": "~1.0.1" - }, - "bin": { - "errno": "cli.js" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-get-iterator/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "node_modules/es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==", - "dev": true, - "dependencies": { - "es6-promise": "^4.0.3" - } - }, - "node_modules/esbuild-wasm": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.17.8.tgz", - "integrity": "sha512-zCmpxv95E0FuCmvdw1K836UHnj4EdiQnFfjTby35y3LAjRPtXMj3sbHDRHjbD8Mqg5lTwq3knacr/1qIFU51CQ==", - "dev": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", - "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.51.0", - "@humanwhocodes/config-array": "^0.11.11", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", - "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jsdoc": { - "version": "45.0.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-45.0.0.tgz", - "integrity": "sha512-l2+Jcs/Ps7oFA+SWY+0sweU/e5LgricnEl6EsDlyRTF5y0+NWL1y9Qwz9PHwHAxtdJq6lxPjEQWmYLMkvhzD4g==", - "dev": true, - "dependencies": { - "@es-joy/jsdoccomment": "~0.39.4", - "are-docs-informative": "^0.0.2", - "comment-parser": "1.3.1", - "debug": "^4.3.4", - "escape-string-regexp": "^4.0.0", - "esquery": "^1.5.0", - "semver": "^7.5.1", - "spdx-expression-parse": "^3.0.1" - }, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-plugin-prefer-arrow": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz", - "integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==", - "dev": true, - "peerDependencies": { - "eslint": ">=2.0.0" - } - }, - "node_modules/eslint-plugin-unused-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.1.0.tgz", - "integrity": "sha512-9l1YFCzXKkw1qtAru1RWUtG2EVDZY0a0eChKXcL+EZ5jitG7qxdctu4RnvhOJHv4xfmUf7h+JJPINlVpGhZMrw==", - "dev": true, - "dependencies": { - "eslint-rule-composer": "^0.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "6 - 7", - "eslint": "8" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - } - } - }, - "node_modules/eslint-rule-composer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", - "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter-asyncresource": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", - "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", - "dev": true - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/exponential-backoff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz", - "integrity": "sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==", - "dev": true - }, - "node_modules/express": { - "version": "4.19.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", - "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", - "dev": true, - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.2", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.6.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/external-editor/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", - "dev": true, - "engines": [ - "node >=0.6.0" - ] - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/fastq": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", - "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-saver-es": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/file-saver-es/-/file-saver-es-2.0.5.tgz", - "integrity": "sha512-Kg0lt+is9nOyi/VDms9miScNGot25jVFbjFccXuCL/shd2Q+rt70MALxHVkXllsX83JEBLiHQNjDPGd/6FIOoQ==" - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "devOptional": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/find-cache-dir": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", - "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", - "dev": true, - "dependencies": { - "commondir": "^1.0.1", - "make-dir": "^3.0.2", - "pkg-dir": "^4.1.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/avajs/find-cache-dir?sponsor=1" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/fs-monkey": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", - "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", - "dev": true - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "dev": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - } - }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "devOptional": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/hammerjs": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz", - "integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "deprecated": "this library is no longer supported", - "dev": true, - "dependencies": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/har-validator/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/har-validator/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/has": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.4.tgz", - "integrity": "sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-ansi/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "dev": true - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hdr-histogram-js": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz", - "integrity": "sha512-Hkn78wwzWHNCp2uarhzQ2SGFLU3JY8SBDDd3TAABK4fc30wm+MuPOrg5QVFVfkKOQd6Bfz3ukJEI+q9sXEkK1g==", - "dev": true, - "dependencies": { - "@assemblyscript/loader": "^0.10.1", - "base64-js": "^1.2.0", - "pako": "^1.0.3" - } - }, - "node_modules/hdr-histogram-percentiles-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz", - "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==", - "dev": true - }, - "node_modules/hosted-git-info": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", - "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", - "dev": true, - "dependencies": { - "lru-cache": "^7.5.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/hosted-git-info/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dev": true, - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ] - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", - "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", - "dev": true - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", - "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", - "dev": true, - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", - "dev": true, - "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - }, - "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dev": true, - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-walk": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.4.tgz", - "integrity": "sha512-t7sv42WkwFkyKbivUCglsQW5YWMskWtbEf4MNKX5u/CCWHKSPzN4FtBQGsQZgCLbxOzpVlcbWVK5KB3auIOjSw==", - "dev": true, - "dependencies": { - "minimatch": "^9.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/ignore-walk/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/ignore-walk/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/image-size": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", - "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", - "dev": true, - "optional": true, - "bin": { - "image-size": "bin/image-size.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/immediate": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", - "dev": true - }, - "node_modules/immutable": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", - "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==", - "dev": true - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ini": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", - "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/inquirer": { - "version": "8.2.4", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", - "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/inquirer/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/inquirer/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/inquirer/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/inquirer/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/inquirer/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "engines": { - "node": ">=12" - } - }, - "node_modules/ionicons": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/ionicons/-/ionicons-6.1.3.tgz", - "integrity": "sha512-ptzz38dd/Yq+PgjhXegh7yhb/SLIk1bvL9vQDtLv1aoSc7alO6mX2DIMgcKYzt9vrNWkRu1f9Jr78zIFFyOXqw==", - "dependencies": { - "@stencil/core": "^2.18.0" - } - }, - "node_modules/ip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.1.tgz", - "integrity": "sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==", - "dev": true - }, - "node_modules/ipaddr.js": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz", - "integrity": "sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", - "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "devOptional": true, - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "devOptional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "devOptional": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "devOptional": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha512-cnS56eR9SPAscL77ik76ATVqoPARTqPIVkMDVxRaWH06zT+6+CzIroYRJ0VVvm0Z1zfAvxvz9i/D3Ppjaqt5Nw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-in-cwd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz", - "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", - "dev": true, - "dependencies": { - "is-path-inside": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-in-cwd/node_modules/is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha512-qhsCR/Esx4U4hg/9I19OVUAJkGWtjRYHMRgUMZE2TDdj+Ag+kttZanLupfddNyglzz50cUlmWzUaI37GDfNx/g==", - "dev": true, - "dependencies": { - "path-is-inside": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-what": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/is-what/-/is-what-3.14.1.tgz", - "integrity": "sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==", - "dev": true - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true - }, - "node_modules/isbinaryfile": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", - "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", - "dev": true, - "engines": { - "node": ">= 8.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/gjtorikian/" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jasmine": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", - "integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==", - "dev": true, - "dependencies": { - "exit": "^0.1.2", - "glob": "^7.0.6", - "jasmine-core": "~2.8.0" - }, - "bin": { - "jasmine": "bin/jasmine.js" - } - }, - "node_modules/jasmine-core": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.5.0.tgz", - "integrity": "sha512-9PMzyvhtocxb3aXJVOPqBDswdgyAeSB81QnLop4npOpbqnheaTEwPc9ZloQeVswugPManznQBjD8kWDTjlnHuw==", - "dev": true - }, - "node_modules/jasmine-spec-reporter": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-7.0.0.tgz", - "integrity": "sha512-OtC7JRasiTcjsaCBPtMO0Tl8glCejM4J4/dNuOJdA8lBjz4PmWjYQ6pzb0uzpBNAWJMDudYuj9OdXJWqM2QTJg==", - "dev": true, - "dependencies": { - "colors": "1.4.0" - } - }, - "node_modules/jasmine/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jasmine/node_modules/jasmine-core": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", - "integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==", - "dev": true - }, - "node_modules/jasminewd2": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", - "integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==", - "dev": true, - "engines": { - "node": ">= 6.9.x" - } - }, - "node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", - "dev": true - }, - "node_modules/jsdoc-type-pratt-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz", - "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==", - "dev": true, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", - "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==" - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", - "dev": true, - "engines": [ - "node >= 0.2.0" - ] - }, - "node_modules/jsprim": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", - "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", - "dev": true, - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.4.0", - "verror": "1.10.0" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/jszip": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", - "dev": true, - "dependencies": { - "lie": "~3.3.0", - "pako": "~1.0.2", - "readable-stream": "~2.3.6", - "setimmediate": "^1.0.5" - } - }, - "node_modules/jszip/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/jszip/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/jszip/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/karma": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.2.tgz", - "integrity": "sha512-C6SU/53LB31BEgRg+omznBEMY4SjHU3ricV6zBcAe1EeILKkeScr+fZXtaI5WyDbkVowJxxAI6h73NcFPmXolQ==", - "dev": true, - "dependencies": { - "@colors/colors": "1.5.0", - "body-parser": "^1.19.0", - "braces": "^3.0.2", - "chokidar": "^3.5.1", - "connect": "^3.7.0", - "di": "^0.0.1", - "dom-serialize": "^2.2.1", - "glob": "^7.1.7", - "graceful-fs": "^4.2.6", - "http-proxy": "^1.18.1", - "isbinaryfile": "^4.0.8", - "lodash": "^4.17.21", - "log4js": "^6.4.1", - "mime": "^2.5.2", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.5", - "qjobs": "^1.2.0", - "range-parser": "^1.2.1", - "rimraf": "^3.0.2", - "socket.io": "^4.4.1", - "source-map": "^0.6.1", - "tmp": "^0.2.1", - "ua-parser-js": "^0.7.30", - "yargs": "^16.1.1" - }, - "bin": { - "karma": "bin/karma" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/karma-chrome-launcher": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz", - "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==", - "dev": true, - "dependencies": { - "which": "^1.2.1" - } - }, - "node_modules/karma-chrome-launcher/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/karma-coverage": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz", - "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.1", - "istanbul-reports": "^3.0.5", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/karma-coverage-istanbul-reporter": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz", - "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^3.0.2", - "minimatch": "^3.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/mattlewis92" - } - }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/karma-coverage-istanbul-reporter/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/karma-jasmine": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz", - "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==", - "dev": true, - "dependencies": { - "jasmine-core": "^4.1.0" - }, - "engines": { - "node": ">=12" - }, - "peerDependencies": { - "karma": "^6.0.0" - } - }, - "node_modules/karma-jasmine-html-reporter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.0.0.tgz", - "integrity": "sha512-SB8HNNiazAHXM1vGEzf8/tSyEhkfxuDdhYdPBX2Mwgzt0OuF2gicApQ+uvXLID/gXyJQgvrM9+1/2SxZFUUDIA==", - "dev": true, - "peerDependencies": { - "jasmine-core": "^4.0.0", - "karma": "^6.0.0", - "karma-jasmine": "^5.0.0" - } - }, - "node_modules/karma-source-map-support": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz", - "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==", - "dev": true, - "dependencies": { - "source-map-support": "^0.5.5" - } - }, - "node_modules/karma/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/karma/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/karma/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/karma/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/karma/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/karma/node_modules/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", - "dev": true, - "dependencies": { - "rimraf": "^3.0.0" - }, - "engines": { - "node": ">=8.17.0" - } - }, - "node_modules/karma/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/karma/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/klona": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", - "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/less": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", - "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", - "dev": true, - "dependencies": { - "copy-anything": "^2.0.1", - "parse-node-version": "^1.0.1", - "tslib": "^2.3.0" - }, - "bin": { - "lessc": "bin/lessc" - }, - "engines": { - "node": ">=6" - }, - "optionalDependencies": { - "errno": "^0.1.1", - "graceful-fs": "^4.1.2", - "image-size": "~0.5.0", - "make-dir": "^2.1.0", - "mime": "^1.4.1", - "needle": "^3.1.0", - "source-map": "~0.6.0" - } - }, - "node_modules/less-loader": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz", - "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==", - "dev": true, - "dependencies": { - "klona": "^2.0.4" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "less": "^3.5.0 || ^4.0.0", - "webpack": "^5.0.0" - } - }, - "node_modules/less/node_modules/make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "optional": true, - "dependencies": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/less/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "optional": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/less/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/license-webpack-plugin": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", - "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", - "dev": true, - "dependencies": { - "webpack-sources": "^3.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-sources": { - "optional": true - } - } - }, - "node_modules/lie": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", - "dev": true, - "dependencies": { - "immediate": "~3.0.5" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", - "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", - "dev": true, - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" - }, - "node_modules/lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-symbols/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/log-symbols/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/log-symbols/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/log-symbols/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/log4js": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", - "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", - "dev": true, - "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "flatted": "^3.2.7", - "rfdc": "^1.3.0", - "streamroller": "^3.1.5" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.29.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.29.0.tgz", - "integrity": "sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/make-fetch-happen": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", - "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", - "dev": true, - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^16.1.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^2.0.3", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^9.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/@npmcli/fs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", - "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", - "dev": true, - "dependencies": { - "@gar/promisify": "^1.1.3", - "semver": "^7.3.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/cacache": { - "version": "16.1.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", - "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", - "dev": true, - "dependencies": { - "@npmcli/fs": "^2.1.0", - "@npmcli/move-file": "^2.0.0", - "chownr": "^2.0.0", - "fs-minipass": "^2.1.0", - "glob": "^8.0.1", - "infer-owner": "^1.0.4", - "lru-cache": "^7.7.1", - "minipass": "^3.1.6", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "mkdirp": "^1.0.4", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^9.0.0", - "tar": "^6.1.11", - "unique-filename": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/make-fetch-happen/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/make-fetch-happen/node_modules/ssri": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", - "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", - "dev": true, - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/unique-filename": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", - "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", - "dev": true, - "dependencies": { - "unique-slug": "^3.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/unique-slug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", - "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "dev": true, - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", - "dev": true - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/mini-css-extract-plugin": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.2.tgz", - "integrity": "sha512-EdlUizq13o0Pd+uCp+WO/JpkLvHRVGt97RqfeGhXqAcorYo1ypJSpkV+WDT0vY/kmh/p7wRdJNJtuyK540PXDw==", - "dev": true, - "dependencies": { - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-fetch": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", - "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", - "dev": true, - "dependencies": { - "minipass": "^3.1.6", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/minipass-fetch/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-fetch/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-flush/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-json-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", - "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", - "dev": true, - "dependencies": { - "jsonparse": "^1.3.1", - "minipass": "^3.0.0" - } - }, - "node_modules/minipass-json-stream/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-json-stream/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dev": true, - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "node_modules/needle": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/needle/-/needle-3.3.1.tgz", - "integrity": "sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==", - "dev": true, - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.3", - "sax": "^1.2.4" - }, - "bin": { - "needle": "bin/needle" - }, - "engines": { - "node": ">= 4.4.x" - } - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true - }, - "node_modules/ng2-charts": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-4.1.1.tgz", - "integrity": "sha512-iHwXDbmX86lfeH8VRcsaW2tJATsuAZo4kvvC/Yk2l35zOHjevja1qBvO6BAibiDazi9r9aS6ZRJOqWPsz1pP2w==", - "dependencies": { - "lodash-es": "^4.17.15", - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/cdk": ">=14.0.0", - "@angular/common": ">=14.0.0", - "@angular/core": ">=14.0.0", - "chart.js": "^3.4.0 || ^4.0.0", - "rxjs": "^6.5.3 || ^7.4.0" - } - }, - "node_modules/ngx-cookie-service": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-15.0.0.tgz", - "integrity": "sha512-KU1JCjfpDTvD6L0FhHN9W/oP3Sue8yMAAK6XY3h/MEhrPS7vx6t3+h0ulY8l8R/9d1cmlQVyTHn1Jd1Jdf5K+g==", - "dependencies": { - "tslib": "^2.0.0" - }, - "peerDependencies": { - "@angular/common": "^15.0.0", - "@angular/core": "^15.0.0" - } - }, - "node_modules/ngx-spinner": { - "version": "15.0.1", - "resolved": "https://registry.npmjs.org/ngx-spinner/-/ngx-spinner-15.0.1.tgz", - "integrity": "sha512-7DjETmBpuXTwI68ad1xMKpwt4Cyz1eyu8E7AJcFiKa+8JAnbo0k1qfvWur0aemncRNxDxHoyl6jw42MsnE/B+g==", - "dependencies": { - "tslib": "^2.3.0" - }, - "peerDependencies": { - "@angular/common": "^15.0.0", - "@angular/core": "^15.0.0" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true, - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-gyp": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", - "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", - "dev": true, - "dependencies": { - "env-paths": "^2.2.0", - "exponential-backoff": "^3.1.1", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^10.0.3", - "nopt": "^6.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": "^12.13 || ^14.13 || >=16" - } - }, - "node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "node_modules/nopt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", - "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", - "dev": true, - "dependencies": { - "abbrev": "^1.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/normalize-package-data": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", - "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", - "dev": true, - "dependencies": { - "hosted-git-info": "^6.0.0", - "is-core-module": "^2.8.1", - "semver": "^7.3.5", - "validate-npm-package-license": "^3.0.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "devOptional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-bundled": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz", - "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==", - "dev": true, - "dependencies": { - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-install-checks": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.3.0.tgz", - "integrity": "sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==", - "dev": true, - "dependencies": { - "semver": "^7.1.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-normalize-package-bin": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.1.tgz", - "integrity": "sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-package-arg": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", - "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^6.0.0", - "proc-log": "^3.0.0", - "semver": "^7.3.5", - "validate-npm-package-name": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-packlist": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", - "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", - "dev": true, - "dependencies": { - "ignore-walk": "^6.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-pick-manifest": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz", - "integrity": "sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA==", - "dev": true, - "dependencies": { - "npm-install-checks": "^6.0.0", - "npm-normalize-package-bin": "^3.0.0", - "npm-package-arg": "^10.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch": { - "version": "14.0.5", - "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.5.tgz", - "integrity": "sha512-kIDMIo4aBm6xg7jOttupWZamsZRkAqMqwqqbVXnUqstY5+tapvv6bkH/qMR76jdgV+YljEUCyWx3hRYMrJiAgA==", - "dev": true, - "dependencies": { - "make-fetch-happen": "^11.0.0", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-json-stream": "^1.0.1", - "minizlib": "^2.1.2", - "npm-package-arg": "^10.0.0", - "proc-log": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/npm-registry-fetch/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/npm-registry-fetch/node_modules/minipass-fetch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", - "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/npm-registry-fetch/node_modules/minipass-fetch/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "dev": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz", - "integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "define-properties": "^1.2.1", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.1.tgz", - "integrity": "sha512-/4b7qZNhv6Uhd7jjnREh1NjnPxlTq+XNWPG88Ydkj5AILcA5m3ajvcg57pB24EQjKv0dK62XnDqk9c/hkIG5Kg==", - "dev": true, - "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ora/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ora/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ora/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ora/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "dev": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dev": true, - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pacote": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.1.0.tgz", - "integrity": "sha512-FFcjtIl+BQNfeliSm7MZz5cpdohvUV1yjGnqgVM4UnVF7JslRY0ImXAygdaCDV0jjUADEWu4y5xsDV8brtrTLg==", - "dev": true, - "dependencies": { - "@npmcli/git": "^4.0.0", - "@npmcli/installed-package-contents": "^2.0.1", - "@npmcli/promise-spawn": "^6.0.1", - "@npmcli/run-script": "^6.0.0", - "cacache": "^17.0.0", - "fs-minipass": "^3.0.0", - "minipass": "^4.0.0", - "npm-package-arg": "^10.0.0", - "npm-packlist": "^7.0.0", - "npm-pick-manifest": "^8.0.0", - "npm-registry-fetch": "^14.0.0", - "proc-log": "^3.0.0", - "promise-retry": "^2.0.1", - "read-package-json": "^6.0.0", - "read-package-json-fast": "^3.0.0", - "sigstore": "^1.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11" - }, - "bin": { - "pacote": "lib/bin.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse-node-version": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", - "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "node_modules/parse5-html-rewriting-stream": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", - "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", - "dev": true, - "dependencies": { - "entities": "^4.3.0", - "parse5": "^7.0.0", - "parse5-sax-parser": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-html-rewriting-stream/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/parse5-html-rewriting-stream/node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parse5-sax-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", - "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", - "dev": true, - "dependencies": { - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-sax-parser/node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/parse5-sax-parser/node_modules/parse5": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", - "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, - "dependencies": { - "entities": "^4.4.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", - "dev": true - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, - "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", - "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "dev": true - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "devOptional": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/piscina": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", - "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", - "dev": true, - "dependencies": { - "eventemitter-asyncresource": "^1.0.0", - "hdr-histogram-js": "^2.0.1", - "hdr-histogram-percentiles-obj": "^3.0.0" - }, - "optionalDependencies": { - "nice-napi": "^1.0.2" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/postcss": { - "version": "8.4.21", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", - "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-loader": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.2.tgz", - "integrity": "sha512-fUJzV/QH7NXUAqV8dWJ9Lg4aTkDCezpTS5HgJ2DvqznexTbSTxgi/dTECvTZ15BwKTtk8G/bqI/QTu2HPd3ZCg==", - "dev": true, - "dependencies": { - "cosmiconfig": "^7.0.0", - "klona": "^2.0.5", - "semver": "^7.3.8" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz", - "integrity": "sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==", - "dev": true, - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.0.tgz", - "integrity": "sha512-SaIbK8XW+MZbd0xHPf7kdfA/3eOt7vxJ72IRecn3EzuZVLr1r0orzf0MX/pN8m+NMDoo6X/SQd8oeKqGZd8PXg==", - "dev": true, - "dependencies": { - "postcss-selector-parser": "^6.0.4" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.15", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", - "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", - "dev": true, - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-bytes": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", - "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", - "dev": true, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "dev": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "dev": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/promise-retry/node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/protractor": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz", - "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==", - "deprecated": "We have news to share - Protractor is deprecated and will reach end-of-life by Summer 2023. To learn more and find out about other options please refer to this post on the Angular blog. Thank you for using and contributing to Protractor. https://goo.gle/state-of-e2e-in-angular", - "dev": true, - "dependencies": { - "@types/q": "^0.0.32", - "@types/selenium-webdriver": "^3.0.0", - "blocking-proxy": "^1.0.0", - "browserstack": "^1.5.1", - "chalk": "^1.1.3", - "glob": "^7.0.3", - "jasmine": "2.8.0", - "jasminewd2": "^2.1.0", - "q": "1.4.1", - "saucelabs": "^1.5.0", - "selenium-webdriver": "3.6.0", - "source-map-support": "~0.4.0", - "webdriver-js-extender": "2.1.0", - "webdriver-manager": "^12.1.7", - "yargs": "^15.3.1" - }, - "bin": { - "protractor": "bin/protractor", - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=10.13.x" - } - }, - "node_modules/protractor/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/protractor/node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/protractor/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/protractor/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "dependencies": { - "source-map": "^0.5.6" - } - }, - "node_modules/protractor/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/protractor/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/protractor/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/protractor/node_modules/wrap-ansi/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/protractor/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/protractor/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", - "dev": true, - "optional": true - }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha512-/CdEdaw49VZVmyIDGUQKDDT53c7qBkO6g5CefWz91Ae+l4+cRtcDYwMTXh6me4O8TMldeGHG3N2Bl84V78Ywbg==", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, - "node_modules/qjobs": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", - "dev": true, - "engines": { - "node": ">=0.9" - } - }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dev": true, - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-package-json": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.4.tgz", - "integrity": "sha512-AEtWXYfopBj2z5N5PbkAOeNHRPUg5q+Nen7QLxV8M2zJq1ym6/lCz3fYNTCXe19puu2d06jfHhrP7v/S2PtMMw==", - "dev": true, - "dependencies": { - "glob": "^10.2.2", - "json-parse-even-better-errors": "^3.0.0", - "normalize-package-data": "^5.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", - "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", - "dev": true, - "dependencies": { - "json-parse-even-better-errors": "^3.0.0", - "npm-normalize-package-bin": "^3.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", - "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/read-package-json/node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.1.tgz", - "integrity": "sha512-aatBvbL26wVUCLmbWdCpeu9iF5wOyWpagiKkInA+kfws3sWdBrTnsvN2CKcyCYyUrc7rebNBlK6+kteg7ksecg==", - "dev": true, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/read-package-json/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/read-package-json/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "devOptional": true, - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/reflect-metadata": { - "version": "0.1.14", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.14.tgz", - "integrity": "sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==", - "dev": true - }, - "node_modules/regenerate": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", - "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true - }, - "node_modules/regenerate-unicode-properties": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz", - "integrity": "sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==", - "dev": true, - "dependencies": { - "regenerate": "^1.4.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, - "node_modules/regex-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.0.tgz", - "integrity": "sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==", - "dev": true - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexpu-core": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz", - "integrity": "sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==", - "dev": true, - "dependencies": { - "@babel/regjsgen": "^0.8.0", - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.1.0", - "regjsparser": "^0.9.1", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/regjsparser": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz", - "integrity": "sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==", - "dev": true, - "dependencies": { - "jsesc": "~0.5.0" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request/node_modules/qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-url-loader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", - "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", - "dev": true, - "dependencies": { - "adjust-sourcemap-loader": "^4.0.0", - "convert-source-map": "^1.7.0", - "loader-utils": "^2.0.0", - "postcss": "^8.2.14", - "source-map": "0.6.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/resolve-url-loader/node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/resolve-url-loader/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", - "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==", - "dev": true - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/roboto-fontface": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz", - "integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g==" - }, - "node_modules/robust-predicates": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", - "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" - }, - "node_modules/rxjs": { - "version": "6.6.7", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", - "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", - "dependencies": { - "tslib": "^1.9.0" - }, - "engines": { - "npm": ">=2.0.0" - } - }, - "node_modules/rxjs/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, - "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-array-concat/node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safe-regex-test": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.1.tgz", - "integrity": "sha512-Y5NejJTTliTyY4H7sipGqY+RX5P87i3F7c4Rcepy72nq+mNLhIsD0W4c7kEmduMDQCSqtPsXPlSTsFhh2LQv+g==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.5", - "get-intrinsic": "^1.2.2", - "is-regex": "^1.1.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/sass": { - "version": "1.58.1", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.1.tgz", - "integrity": "sha512-bnINi6nPXbP1XNRaranMFEBZWUfdW/AF16Ql5+ypRxfTvCRTTKrLsMIakyDcayUt2t/RZotmL4kgJwNH5xO+bg==", - "dev": true, - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/sass-loader": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", - "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==", - "dev": true, - "dependencies": { - "klona": "^2.0.4", - "neo-async": "^2.6.2" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", - "sass": "^1.3.0", - "sass-embedded": "*", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - } - } - }, - "node_modules/saucelabs": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz", - "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", - "dev": true, - "dependencies": { - "https-proxy-agent": "^2.2.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/saucelabs/node_modules/agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "dependencies": { - "es6-promisify": "^5.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/saucelabs/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/saucelabs/node_modules/https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "dev": true, - "dependencies": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "engines": { - "node": ">= 4.5.0" - } - }, - "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", - "dev": true - }, - "node_modules/schema-utils": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", - "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true - }, - "node_modules/selenium-webdriver": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", - "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", - "dev": true, - "dependencies": { - "jszip": "^3.1.3", - "rimraf": "^2.5.4", - "tmp": "0.0.30", - "xml2js": "^0.4.17" - }, - "engines": { - "node": ">= 6.9.0" - } - }, - "node_modules/selenium-webdriver/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/selenium-webdriver/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/selenium-webdriver/node_modules/tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "dev": true, - "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "dev": true, - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dev": true, - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", - "dev": true - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/sigstore": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.9.0.tgz", - "integrity": "sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==", - "dev": true, - "dependencies": { - "@sigstore/bundle": "^1.1.0", - "@sigstore/protobuf-specs": "^0.2.0", - "@sigstore/sign": "^1.0.0", - "@sigstore/tuf": "^1.0.3", - "make-fetch-happen": "^11.0.1" - }, - "bin": { - "sigstore": "bin/sigstore.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/sigstore/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/sigstore/node_modules/make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/sigstore/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sigstore/node_modules/minipass-fetch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", - "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/sigstore/node_modules/minipass-fetch/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socket.io": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.3.tgz", - "integrity": "sha512-SE+UIQXBQE+GPG2oszWMlsEmWtHVqw/h1VrYJGK5/MC7CH5p58N448HwIrtREcvR4jfdOJAY4ieQfxMr55qbbw==", - "dev": true, - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.5.2", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", - "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", - "dev": true, - "dependencies": { - "ws": "~8.11.0" - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/sockjs/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "dev": true, - "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", - "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", - "dev": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-loader": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.1.tgz", - "integrity": "sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA==", - "dev": true, - "dependencies": { - "abab": "^2.0.6", - "iconv-lite": "^0.6.3", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.72.1" - } - }, - "node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead" - }, - "node_modules/spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.16", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz", - "integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==", - "dev": true - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/sshpk": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", - "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "bin": { - "sshpk-conv": "bin/sshpk-conv", - "sshpk-sign": "bin/sshpk-sign", - "sshpk-verify": "bin/sshpk-verify" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ssri": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.5.tgz", - "integrity": "sha512-bSf16tAFkGeRlUNDjXu8FzaMQt6g2HZJrun7mtMbIPOddxt3GLMSz5VWUWcqTJUPfLEaDIepGxv+bYQW49596A==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/ssri/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", - "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/streamroller": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", - "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", - "dev": true, - "dependencies": { - "date-format": "^4.0.14", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", - "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", - "dev": true, - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/terser": { - "version": "5.16.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.3.tgz", - "integrity": "sha512-v8wWLaS/xt3nE9dgKEWhNUFP6q4kngO5B8eYFUuebsu7Dw/UNAnpUod6UHo04jSSkv8TzKHjZDSd7EXdDQAl8Q==", - "dev": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.10", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", - "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.20", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.26.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/terser-webpack-plugin/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser-webpack-plugin/node_modules/terser": { - "version": "5.26.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", - "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", - "dev": true, - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "devOptional": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tuf-js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.7.tgz", - "integrity": "sha512-i3P9Kgw3ytjELUfpuKVDNBJvk4u5bXL6gskv572mcevPbSKCV3zt3djhmlEQ65yERjIbOSncy7U4cQJaB1CBCg==", - "dev": true, - "dependencies": { - "@tufjs/models": "1.0.4", - "debug": "^4.3.4", - "make-fetch-happen": "^11.1.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/tuf-js/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/tuf-js/node_modules/make-fetch-happen": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz", - "integrity": "sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w==", - "dev": true, - "dependencies": { - "agentkeepalive": "^4.2.1", - "cacache": "^17.0.0", - "http-cache-semantics": "^4.1.1", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^7.7.1", - "minipass": "^5.0.0", - "minipass-fetch": "^3.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^7.0.0", - "ssri": "^10.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/tuf-js/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/tuf-js/node_modules/minipass-fetch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", - "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", - "dev": true, - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "optionalDependencies": { - "encoding": "^0.1.13" - } - }, - "node_modules/tuf-js/node_modules/minipass-fetch/node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "dev": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-assert": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", - "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", - "dev": true - }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/ua-parser-js": { - "version": "0.7.37", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.37.tgz", - "integrity": "sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "engines": { - "node": "*" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/unicode-canonical-property-names-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", - "integrity": "sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-ecmascript": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", - "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", - "dev": true, - "dependencies": { - "unicode-canonical-property-names-ecmascript": "^2.0.0", - "unicode-property-aliases-ecmascript": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-match-property-value-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz", - "integrity": "sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", - "dev": true, - "dependencies": { - "unique-slug": "^4.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", - "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/validate-npm-package-name": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", - "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", - "dev": true, - "dependencies": { - "builtins": "^5.0.0" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/verror/node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", - "dev": true - }, - "node_modules/void-elements": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz", - "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webdriver-js-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz", - "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==", - "dev": true, - "dependencies": { - "@types/selenium-webdriver": "^3.0.0", - "selenium-webdriver": "^3.0.1" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/webdriver-manager": { - "version": "12.1.9", - "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.9.tgz", - "integrity": "sha512-Yl113uKm8z4m/KMUVWHq1Sjtla2uxEBtx2Ue3AmIlnlPAKloDn/Lvmy6pqWCUersVISpdMeVpAaGbNnvMuT2LQ==", - "dev": true, - "dependencies": { - "adm-zip": "^0.5.2", - "chalk": "^1.1.1", - "del": "^2.2.0", - "glob": "^7.0.3", - "ini": "^1.3.4", - "minimist": "^1.2.0", - "q": "^1.4.1", - "request": "^2.87.0", - "rimraf": "^2.5.2", - "semver": "^5.3.0", - "xml2js": "^0.4.17" - }, - "bin": { - "webdriver-manager": "bin/webdriver-manager" - }, - "engines": { - "node": ">=6.9.x" - } - }, - "node_modules/webdriver-manager/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/webdriver-manager/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "node_modules/webdriver-manager/node_modules/rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/webdriver-manager/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/webdriver-manager/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/webdriver-manager/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/webpack": { - "version": "5.76.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.1.tgz", - "integrity": "sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-middleware": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.0.1.tgz", - "integrity": "sha512-PZPZ6jFinmqVPJZbisfggDiC+2EeGZ1ZByyMP5sOFJcPPWSexalISz+cvm+j+oYPT7FIJyxT76esjnw9DhE5sw==", - "dev": true, - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.12", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/webpack-dev-server": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz", - "integrity": "sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==", - "dev": true, - "dependencies": { - "@types/bonjour": "^3.5.9", - "@types/connect-history-api-fallback": "^1.3.5", - "@types/express": "^4.17.13", - "@types/serve-index": "^1.9.1", - "@types/serve-static": "^1.13.10", - "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.0.11", - "chokidar": "^3.5.3", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "default-gateway": "^6.0.3", - "express": "^4.17.3", - "graceful-fs": "^4.2.6", - "html-entities": "^2.3.2", - "http-proxy-middleware": "^2.0.3", - "ipaddr.js": "^2.0.1", - "open": "^8.0.9", - "p-retry": "^4.5.0", - "rimraf": "^3.0.2", - "schema-utils": "^4.0.0", - "selfsigned": "^2.1.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.37.0 || ^5.0.0" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", - "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", - "dev": true, - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^3.4.3", - "mime-types": "^2.1.31", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^4.0.0 || ^5.0.0" - } - }, - "node_modules/webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", - "dev": true, - "dependencies": { - "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-subresource-integrity": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", - "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", - "dev": true, - "dependencies": { - "typed-assert": "^1.0.8" - }, - "engines": { - "node": ">= 12" - }, - "peerDependencies": { - "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", - "webpack": "^5.12.0" - }, - "peerDependenciesMeta": { - "html-webpack-plugin": { - "optional": true - } - } - }, - "node_modules/webpack/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/webpack/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/webpack/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/webpack/node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, - "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "dev": true, - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/yargs": { - "version": "17.6.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", - "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zone.js": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.0.tgz", - "integrity": "sha512-7m3hNNyswsdoDobCkYNAy5WiUulkMd3+fWaGT9ij6iq3Zr/IwJo4RMCYPSDjT+r7tnPErmY9sZpKhWQ8S5k6XQ==", - "dependencies": { - "tslib": "^2.3.0" - } + "name": "openems-ui", + "version": "2024.5.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "openems-ui", + "version": "2024.5.0", + "license": "AGPL-3.0", + "dependencies": { + "@angular/animations": "~16.2.12", + "@angular/common": "~16.2.12", + "@angular/core": "~16.2.12", + "@angular/forms": "~16.2.12", + "@angular/platform-browser": "~16.2.12", + "@angular/platform-browser-dynamic": "~16.2.12", + "@angular/router": "~16.2.12", + "@angular/service-worker": "~16.2.12", + "@ionic/angular": "^6.7.5", + "@ngx-formly/core": "^6.3.0", + "@ngx-formly/ionic": "^6.3.0", + "@ngx-formly/schematics": "^6.3.0", + "@ngx-translate/core": "^14.0.0", + "@nodro7/angular-mydatepicker": "^0.14.0", + "chart.js": "^4.4.2", + "chartjs-adapter-date-fns": "^3.0.0", + "chartjs-plugin-zoom": "^2.0.1", + "classlist.js": "^1.1.20150312", + "compare-versions": "^6.1.0", + "d3": "^7.9.0", + "date-fns": "^2.30.0", + "file-saver-es": "^2.0.5", + "ng2-charts": "4.1.1", + "ngx-cookie-service": "^16.1.0", + "ngx-spinner": "^16.0.2", + "roboto-fontface": "^0.10.0", + "rxjs": "~6.6.7", + "swiper": "11.1.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1", + "zone.js": "~0.13.3" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^16.2.13", + "@angular-eslint/builder": "^16.3.1", + "@angular-eslint/eslint-plugin": "^16.3.1", + "@angular-eslint/eslint-plugin-template": "^16.3.1", + "@angular-eslint/template-parser": "^16.3.1", + "@angular/cli": "^16.2.13", + "@angular/compiler": "^16.2.12", + "@angular/compiler-cli": "^16.2.12", + "@angular/language-service": "^16.2.12", + "@ionic/angular-toolkit": "^11.0.1", + "@types/jasmine": "~4.3.6", + "@types/jasminewd2": "~2.0.13", + "@types/node": "^20.12.6", + "@types/uuid": "^9.0.8", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint": "^8.57.0", + "eslint-plugin-import": "2.29.1", + "eslint-plugin-jsdoc": "48.2.3", + "eslint-plugin-prefer-arrow": "1.2.3", + "eslint-plugin-unused-imports": "^3.1.0", + "jasmine-core": "~4.5.0", + "jasmine-spec-reporter": "~7.0.0", + "karma": "~6.4.2", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.1", + "karma-coverage-istanbul-reporter": "~3.0.3", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "^2.1.0", + "protractor": "~7.0.0", + "ts-node": "^10.9.2", + "typescript": "~4.9.5", + "typescript-strict-plugin": "^2.4.1" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1602.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "16.2.13", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "7.8.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/build-angular": { + "version": "16.2.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "2.2.1", + "@angular-devkit/architect": "0.1602.13", + "@angular-devkit/build-webpack": "0.1602.13", + "@angular-devkit/core": "16.2.13", + "@babel/core": "7.22.9", + "@babel/generator": "7.22.9", + "@babel/helper-annotate-as-pure": "7.22.5", + "@babel/helper-split-export-declaration": "7.22.6", + "@babel/plugin-proposal-async-generator-functions": "7.20.7", + "@babel/plugin-transform-async-to-generator": "7.22.5", + "@babel/plugin-transform-runtime": "7.22.9", + "@babel/preset-env": "7.22.9", + "@babel/runtime": "7.22.6", + "@babel/template": "7.22.5", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "16.2.13", + "@vitejs/plugin-basic-ssl": "1.0.1", + "ansi-colors": "4.1.3", + "autoprefixer": "10.4.14", + "babel-loader": "9.1.3", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.21.5", + "chokidar": "3.5.3", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.20", + "css-loader": "6.8.1", + "esbuild-wasm": "0.18.17", + "fast-glob": "3.3.1", + "guess-parser": "0.4.22", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.2.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.1.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.1", + "magic-string": "0.30.1", + "mini-css-extract-plugin": "2.7.6", + "mrmime": "1.0.1", + "open": "8.4.2", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "7.0.0", + "picomatch": "2.3.1", + "piscina": "4.0.0", + "postcss": "8.4.31", + "postcss-loader": "7.3.3", + "resolve-url-loader": "5.0.0", + "rxjs": "7.8.1", + "sass": "1.64.1", + "sass-loader": "13.3.2", + "semver": "7.5.4", + "source-map-loader": "4.0.1", + "source-map-support": "0.5.21", + "terser": "5.19.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.6.1", + "vite": "4.5.2", + "webpack": "5.88.2", + "webpack-dev-middleware": "6.1.2", + "webpack-dev-server": "4.15.1", + "webpack-merge": "5.9.0", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.18.17" + }, + "peerDependencies": { + "@angular/compiler-cli": "^16.0.0", + "@angular/localize": "^16.0.0", + "@angular/platform-server": "^16.0.0", + "@angular/service-worker": "^16.0.0", + "jest": "^29.5.0", + "jest-environment-jsdom": "^29.5.0", + "karma": "^6.3.0", + "ng-packagr": "^16.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=4.9.3 <5.2" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/platform-server": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "jest": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@ampproject/remapping": { + "version": "2.2.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { + "version": "7.22.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.5", + "@babel/generator": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.6", + "@babel/parser": "^7.22.7", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.8", + "@babel/types": "^7.22.5", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/runtime": { + "version": "7.22.6", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@babel/template": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv-keywords": { + "version": "3.5.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/build-angular/node_modules/fast-glob": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/build-angular/node_modules/jsonc-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/build-angular/node_modules/less": { + "version": "4.1.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/postcss": { + "version": "8.4.31", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/regenerator-runtime": { + "version": "0.13.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "7.8.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/schema-utils": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/semver": { + "version": "7.5.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/terser": { + "version": "5.19.2", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/tslib": { + "version": "2.6.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/@angular-devkit/build-angular/node_modules/webpack": { + "version": "5.88.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/@angular-devkit/build-webpack": { + "version": "0.1602.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.1602.13", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "7.8.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/core": { + "version": "16.2.13", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.12.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.2.0", + "picomatch": "2.3.1", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/core/node_modules/jsonc-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/schematics": { + "version": "16.2.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "16.2.13", + "jsonc-parser": "3.2.0", + "magic-string": "0.30.1", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/jsonc-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-eslint/builder": { + "version": "16.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/devkit": "16.5.1", + "nx": "16.5.1" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/bundled-angular-compiler": { + "version": "16.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-eslint/eslint-plugin": { + "version": "16.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-eslint/utils": "16.3.1", + "@typescript-eslint/utils": "5.62.0" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template": { + "version": "16.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "16.3.1", + "@angular-eslint/utils": "16.3.1", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "aria-query": "5.3.0", + "axobject-query": "4.0.0" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/template-parser": { + "version": "16.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "16.3.1", + "eslint-scope": "^7.0.0" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/template-parser/node_modules/eslint-scope": { + "version": "7.2.2", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@angular-eslint/template-parser/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@angular-eslint/utils": { + "version": "16.3.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "16.3.1", + "@typescript-eslint/utils": "5.62.0" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular/animations": { + "version": "16.2.12", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/core": "16.2.12" + } + }, + "node_modules/@angular/cdk": { + "version": "15.2.9", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "optionalDependencies": { + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@angular/common": "^15.0.0 || ^16.0.0", + "@angular/core": "^15.0.0 || ^16.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/cdk/node_modules/parse5": { + "version": "7.1.2", + "license": "MIT", + "optional": true, + "peer": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/@angular/cli": { + "version": "16.2.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": "0.1602.13", + "@angular-devkit/core": "16.2.13", + "@angular-devkit/schematics": "16.2.13", + "@schematics/angular": "16.2.13", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.3", + "ini": "4.1.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.2.0", + "npm-package-arg": "10.1.0", + "npm-pick-manifest": "8.0.1", + "open": "8.4.2", + "ora": "5.4.1", + "pacote": "15.2.0", + "resolve": "1.22.2", + "semver": "7.5.4", + "symbol-observable": "4.0.0", + "yargs": "17.7.2" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli/node_modules/jsonc-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular/cli/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular/cli/node_modules/resolve": { + "version": "1.22.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/@angular/cli/node_modules/semver": { + "version": "7.5.4", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular/cli/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/@angular/common": { + "version": "16.2.12", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/core": "16.2.12", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "16.2.12", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/core": "16.2.12" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/compiler-cli": { + "version": "16.2.12", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "7.23.2", + "@jridgewell/sourcemap-codec": "^1.4.14", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/compiler": "16.2.12", + "typescript": ">=4.9.3 <5.2" + } + }, + "node_modules/@angular/core": { + "version": "16.2.12", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.13.0" + } + }, + "node_modules/@angular/forms": { + "version": "16.2.12", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": "16.2.12", + "@angular/core": "16.2.12", + "@angular/platform-browser": "16.2.12", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/language-service": { + "version": "16.2.12", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.10.0" + } + }, + "node_modules/@angular/platform-browser": { + "version": "16.2.12", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/animations": "16.2.12", + "@angular/common": "16.2.12", + "@angular/core": "16.2.12" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "16.2.12", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": "16.2.12", + "@angular/compiler": "16.2.12", + "@angular/core": "16.2.12", + "@angular/platform-browser": "16.2.12" + } + }, + "node_modules/@angular/router": { + "version": "16.2.12", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": "16.2.12", + "@angular/core": "16.2.12", + "@angular/platform-browser": "16.2.12", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/service-worker": { + "version": "16.2.12", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "bin": { + "ngsw-config": "ngsw-config.js" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0" + }, + "peerDependencies": { + "@angular/common": "16.2.12", + "@angular/core": "16.2.12" + } + }, + "node_modules/@assemblyscript/loader": { + "version": "0.10.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.0", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.0", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/@babel/generator": { + "version": "7.24.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.22.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.22.15", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.24.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5", + "@babel/helper-replace-supers": "^7.24.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.22.15", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "regexpu-core": "^5.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.23.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-wrap-function": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-member-expression-to-functions": "^7.23.0", + "@babel/helper-optimise-call-expression": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.22.20", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-function-name": "^7.22.5", + "@babel/template": "^7.22.15", + "@babel/types": "^7.22.19" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.1", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.4", + "dev": true, + "license": "MIT", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.20.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.20.2", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.24.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-remap-async-to-generator": "^7.22.20", + "@babel/plugin-syntax-async-generators": "^7.8.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-remap-async-to-generator": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.24.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.24.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.24.4", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1", + "@babel/helper-split-export-declaration": "^7.22.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/template": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-json-strings": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-simple-access": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.22.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-replace-supers": "^7.24.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.22.5", + "@babel/helper-create-class-features-plugin": "^7.24.1", + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "regenerator-transform": "^0.15.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.22.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-plugin-utils": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.22.15", + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.22.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-compilation-targets": "^7.22.9", + "@babel/helper-plugin-utils": "^7.22.5", + "@babel/helper-validator-option": "^7.22.5", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.22.5", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.22.5", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-dynamic-import": "^7.8.3", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-import-assertions": "^7.22.5", + "@babel/plugin-syntax-import-attributes": "^7.22.5", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.22.5", + "@babel/plugin-transform-async-generator-functions": "^7.22.7", + "@babel/plugin-transform-async-to-generator": "^7.22.5", + "@babel/plugin-transform-block-scoped-functions": "^7.22.5", + "@babel/plugin-transform-block-scoping": "^7.22.5", + "@babel/plugin-transform-class-properties": "^7.22.5", + "@babel/plugin-transform-class-static-block": "^7.22.5", + "@babel/plugin-transform-classes": "^7.22.6", + "@babel/plugin-transform-computed-properties": "^7.22.5", + "@babel/plugin-transform-destructuring": "^7.22.5", + "@babel/plugin-transform-dotall-regex": "^7.22.5", + "@babel/plugin-transform-duplicate-keys": "^7.22.5", + "@babel/plugin-transform-dynamic-import": "^7.22.5", + "@babel/plugin-transform-exponentiation-operator": "^7.22.5", + "@babel/plugin-transform-export-namespace-from": "^7.22.5", + "@babel/plugin-transform-for-of": "^7.22.5", + "@babel/plugin-transform-function-name": "^7.22.5", + "@babel/plugin-transform-json-strings": "^7.22.5", + "@babel/plugin-transform-literals": "^7.22.5", + "@babel/plugin-transform-logical-assignment-operators": "^7.22.5", + "@babel/plugin-transform-member-expression-literals": "^7.22.5", + "@babel/plugin-transform-modules-amd": "^7.22.5", + "@babel/plugin-transform-modules-commonjs": "^7.22.5", + "@babel/plugin-transform-modules-systemjs": "^7.22.5", + "@babel/plugin-transform-modules-umd": "^7.22.5", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.22.5", + "@babel/plugin-transform-new-target": "^7.22.5", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.22.5", + "@babel/plugin-transform-numeric-separator": "^7.22.5", + "@babel/plugin-transform-object-rest-spread": "^7.22.5", + "@babel/plugin-transform-object-super": "^7.22.5", + "@babel/plugin-transform-optional-catch-binding": "^7.22.5", + "@babel/plugin-transform-optional-chaining": "^7.22.6", + "@babel/plugin-transform-parameters": "^7.22.5", + "@babel/plugin-transform-private-methods": "^7.22.5", + "@babel/plugin-transform-private-property-in-object": "^7.22.5", + "@babel/plugin-transform-property-literals": "^7.22.5", + "@babel/plugin-transform-regenerator": "^7.22.5", + "@babel/plugin-transform-reserved-words": "^7.22.5", + "@babel/plugin-transform-shorthand-properties": "^7.22.5", + "@babel/plugin-transform-spread": "^7.22.5", + "@babel/plugin-transform-sticky-regex": "^7.22.5", + "@babel/plugin-transform-template-literals": "^7.22.5", + "@babel/plugin-transform-typeof-symbol": "^7.22.5", + "@babel/plugin-transform-unicode-escapes": "^7.22.5", + "@babel/plugin-transform-unicode-property-regex": "^7.22.5", + "@babel/plugin-transform-unicode-regex": "^7.22.5", + "@babel/plugin-transform-unicode-sets-regex": "^7.22.5", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.22.5", + "babel-plugin-polyfill-corejs2": "^0.4.4", + "babel-plugin-polyfill-corejs3": "^0.8.2", + "babel-plugin-polyfill-regenerator": "^0.5.1", + "core-js-compat": "^3.31.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/regjsgen": { + "version": "0.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/runtime": { + "version": "7.24.0", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.24.1", + "@babel/generator": "^7.24.1", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.1", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.24.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.24.0", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.24.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.42.0", + "dev": true, + "license": "MIT", + "dependencies": { + "comment-parser": "1.4.1", + "esquery": "^1.5.0", + "jsdoc-type-pratt-parser": "~4.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.17", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.24.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/type-fest": { + "version": "0.20.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@ionic/angular": { + "version": "6.7.5", + "license": "MIT", + "dependencies": { + "@ionic/core": "6.7.5", + "ionicons": "^6.1.3", + "jsonc-parser": "^3.0.0", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/core": ">=12.0.0", + "@angular/forms": ">=12.0.0", + "@angular/router": ">=12.0.0", + "rxjs": ">=6.6.0", + "zone.js": ">=0.11.0" + } + }, + "node_modules/@ionic/angular-toolkit": { + "version": "11.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "^17.0.0", + "@angular-devkit/schematics": "^17.0.0", + "@schematics/angular": "^17.0.0" + } + }, + "node_modules/@ionic/angular-toolkit/node_modules/@angular-devkit/core": { + "version": "17.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "8.12.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.2.1", + "picomatch": "4.0.1", + "rxjs": "7.8.1", + "source-map": "0.7.4" + }, + "engines": { + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@ionic/angular-toolkit/node_modules/@angular-devkit/schematics": { + "version": "17.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "17.3.3", + "jsonc-parser": "3.2.1", + "magic-string": "0.30.8", + "ora": "5.4.1", + "rxjs": "7.8.1" + }, + "engines": { + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@ionic/angular-toolkit/node_modules/@schematics/angular": { + "version": "17.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "17.3.3", + "@angular-devkit/schematics": "17.3.3", + "jsonc-parser": "3.2.1" + }, + "engines": { + "node": "^18.13.0 || >=20.9.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@ionic/angular-toolkit/node_modules/magic-string": { + "version": "0.30.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@ionic/angular-toolkit/node_modules/picomatch": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@ionic/angular-toolkit/node_modules/rxjs": { + "version": "7.8.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@ionic/core": { + "version": "6.7.5", + "license": "MIT", + "dependencies": { + "@stencil/core": "^2.18.0", + "ionicons": "^6.1.3", + "tslib": "^2.1.0" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "license": "MIT" + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@ngtools/webpack": { + "version": "16.2.13", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^16.0.0", + "typescript": ">=4.9.3 <5.2", + "webpack": "^5.54.0" + } + }, + "node_modules/@ngx-formly/core": { + "version": "6.3.0", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/forms": ">=13.2.0", + "rxjs": "^6.5.3 || ^7.0.0" + } + }, + "node_modules/@ngx-formly/ionic": { + "version": "6.3.0", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@ionic/angular": "^6.0.0 || ^7.0.0", + "@ngx-formly/core": "6.3.0" + } + }, + "node_modules/@ngx-formly/schematics": { + "version": "6.3.0", + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "^13.0.3", + "@angular-devkit/schematics": "^13.0.3", + "@schematics/angular": "^13.0.3" + } + }, + "node_modules/@ngx-formly/schematics/node_modules/@angular-devkit/core": { + "version": "13.3.11", + "license": "MIT", + "dependencies": { + "ajv": "8.9.0", + "ajv-formats": "2.1.1", + "fast-json-stable-stringify": "2.1.0", + "magic-string": "0.25.7", + "rxjs": "6.6.7", + "source-map": "0.7.3" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@ngx-formly/schematics/node_modules/@angular-devkit/schematics": { + "version": "13.3.11", + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "13.3.11", + "jsonc-parser": "3.0.0", + "magic-string": "0.25.7", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@ngx-formly/schematics/node_modules/@schematics/angular": { + "version": "13.3.11", + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "13.3.11", + "@angular-devkit/schematics": "13.3.11", + "jsonc-parser": "3.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@ngx-formly/schematics/node_modules/ajv": { + "version": "8.9.0", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@ngx-formly/schematics/node_modules/jsonc-parser": { + "version": "3.0.0", + "license": "MIT" + }, + "node_modules/@ngx-formly/schematics/node_modules/magic-string": { + "version": "0.25.7", + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/@ngx-formly/schematics/node_modules/source-map": { + "version": "0.7.3", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@ngx-translate/core": { + "version": "14.0.0", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": ">=13.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodro7/angular-mydatepicker": { + "version": "0.14.0", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "3.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^6.0.0", + "lru-cache": "^7.4.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@npmcli/git/node_modules/which": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "lib/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/run-script/node_modules/which": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@nrwl/devkit": { + "version": "16.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@nx/devkit": "16.5.1" + } + }, + "node_modules/@nrwl/tao": { + "version": "16.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "nx": "16.5.1" + }, + "bin": { + "tao": "index.js" + } + }, + "node_modules/@nx/devkit": { + "version": "16.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@nrwl/devkit": "16.5.1", + "ejs": "^3.1.7", + "ignore": "^5.0.4", + "semver": "7.5.3", + "tmp": "~0.2.1", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "nx": ">= 15 <= 17" + } + }, + "node_modules/@nx/devkit/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/devkit/node_modules/semver": { + "version": "7.5.3", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@nx/devkit/node_modules/tmp": { + "version": "0.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@nx/devkit/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/@nx/nx-linux-x64-gnu": { + "version": "16.5.1", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nx/nx-linux-x64-musl": { + "version": "16.5.1", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.0.4", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^3.2.1", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@schematics/angular": { + "version": "16.2.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": "16.2.13", + "@angular-devkit/schematics": "16.2.13", + "jsonc-parser": "3.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@schematics/angular/node_modules/jsonc-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@sigstore/bundle": { + "version": "1.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.2.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "1.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "make-fetch-happen": "^11.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign/node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@sigstore/sign/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@sigstore/sign/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@sigstore/sign/node_modules/make-fetch-happen": { + "version": "11.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign/node_modules/minipass-fetch": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/@sigstore/sign/node_modules/minipass-fetch/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@sigstore/tuf": { + "version": "1.0.3", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0", + "tuf-js": "^1.1.7" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@stencil/core": { + "version": "2.22.3", + "license": "MIT", + "bin": { + "stencil": "bin/stencil" + }, + "engines": { + "node": ">=12.10.0", + "npm": ">=6.0.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@tufjs/canonical-json": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "1.0.0", + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@tufjs/models/node_modules/minimatch": { + "version": "9.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cors": { + "version": "2.8.17", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.56.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/express": { + "version": "4.17.21", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.19.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/http-proxy": { + "version": "1.17.14", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jasmine": { + "version": "4.3.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jasminewd2": { + "version": "2.0.13", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jasmine": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.12.7", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/q": { + "version": "0.0.32", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/qs": { + "version": "6.9.14", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/selenium-webdriver": { + "version": "3.0.26", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "0.17.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-index": { + "version": "1.9.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, + "node_modules/@types/sockjs": { + "version": "0.3.36", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/type-utils/node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/globby": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/slash": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/@vitejs/plugin-basic-ssl": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.6.0" + }, + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "dev": true, + "license": "MIT" + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@wessberg/ts-evaluator": { + "version": "0.0.27", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "jsdom": "^16.4.0", + "object-path": "^0.11.5", + "tslib": "^2.0.3" + }, + "engines": { + "node": ">=10.1.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/wessberg/ts-evaluator?sponsor=1" + }, + "peerDependencies": { + "typescript": ">=3.2.x || >= 4.x" + } + }, + "node_modules/@wessberg/ts-evaluator/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wessberg/ts-evaluator/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@wessberg/ts-evaluator/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@wessberg/ts-evaluator/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@wessberg/ts-evaluator/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/@yarnpkg/parsers": { + "version": "3.0.0-rc.46", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "js-yaml": "^3.10.0", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=14.15.0" + } + }, + "node_modules/@zkochan/js-yaml": { + "version": "0.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@zkochan/js-yaml/node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/abab": { + "version": "2.0.6", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/accepts": { + "version": "1.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^7.1.1", + "acorn-walk": "^7.1.1" + } + }, + "node_modules/acorn-globals/node_modules/acorn": { + "version": "7.4.1", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals/node_modules/acorn-walk": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adjust-sourcemap-loader": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "regex-parser": "^2.2.11" + }, + "engines": { + "node": ">=8.9" + } + }, + "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.12", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-html-community": { + "version": "0.0.8", + "dev": true, + "engines": [ + "node >= 0.8.0" + ], + "license": "Apache-2.0", + "bin": { + "ansi-html": "bin/ansi-html" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "devOptional": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/are-docs-informative": { + "version": "0.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/array-includes": { + "version": "3.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/async": { + "version": "3.2.5", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.4.14", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.21.5", + "caniuse-lite": "^1.0.30001464", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.12.0", + "dev": true, + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.6.8", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/axobject-query": { + "version": "4.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/babel-loader": { + "version": "9.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "find-cache-dir": "^4.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.22.6", + "@babel/helper-define-polyfill-provider": "^0.6.1", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.8.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.4.4", + "core-js-compat": "^3.33.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.5.5", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.5.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator/node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.22.6", + "@babel/helper-plugin-utils": "^7.22.5", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/base64id": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "dev": true, + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/big.js": { + "version": "5.2.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/blocking-proxy": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "blocking-proxy": "built/lib/bin.js" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/body-parser": { + "version": "1.20.2", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/bonjour-service": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-process-hrtime": { + "version": "1.0.0", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/browserslist": { + "version": "4.23.0", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/browserstack": { + "version": "1.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^2.2.1" + } + }, + "node_modules/browserstack/node_modules/agent-base": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/browserstack/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/browserstack/node_modules/https-proxy-agent": { + "version": "2.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/builtin-modules": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/builtins": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/bytes": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "17.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^7.7.1", + "minipass": "^7.0.3", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001606", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/caseless": { + "version": "0.12.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/chalk": { + "version": "2.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "dev": true, + "license": "MIT" + }, + "node_modules/chart.js": { + "version": "4.4.2", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chartjs-adapter-date-fns": { + "version": "3.0.0", + "license": "MIT", + "peerDependencies": { + "chart.js": ">=2.8.0", + "date-fns": ">=2.0.0" + } + }, + "node_modules/chartjs-plugin-zoom": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "hammerjs": "^2.0.8" + }, + "peerDependencies": { + "chart.js": ">=3.2.0" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/classlist.js": { + "version": "1.1.20150312", + "license": "Dedicated to the public domain" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-convert/node_modules/color-name": { + "version": "1.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/color-name": { + "version": "1.1.4", + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.20", + "dev": true, + "license": "MIT" + }, + "node_modules/colors": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/comment-parser": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/common-path-prefix": { + "version": "3.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/compare-versions": { + "version": "6.1.0", + "license": "MIT" + }, + "node_modules/compressible": { + "version": "2.0.18", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.7.4", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/compression/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/compression/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/compression/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/connect": { + "version": "3.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/connect/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/finalhandler": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/connect/node_modules/on-finished": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/connect/node_modules/statuses": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^3.14.1" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/core-js-compat": { + "version": "3.36.1", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cosmiconfig": { + "version": "8.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0", + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig/node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/cosmiconfig/node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/critters": { + "version": "0.0.20", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^5.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.2", + "htmlparser2": "^8.0.2", + "postcss": "^8.4.23", + "pretty-bytes": "^5.3.0" + } + }, + "node_modules/critters/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/critters/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/critters/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/critters/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/critters/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "6.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.21", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.3", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cssom": { + "version": "0.4.4", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/custom-event": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.0", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.0.0", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.3", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "2.30.0", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/date-format": { + "version": "4.0.14", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "dev": true, + "license": "MIT" + }, + "node_modules/default-gateway": { + "version": "6.0.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-lazy-prop": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/del": { + "version": "2.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/array-union": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/del/node_modules/globby": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/pify": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/del/node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/delaunator": { + "version": "5.0.0", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/depd": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/di": { + "version": "0.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dns-packet": { + "version": "5.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serialize": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domexception": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/domexception/node_modules/webidl-conversions": { + "version": "5.0.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/domhandler": { + "version": "5.0.3", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "10.0.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.9", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.728", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/ws": { + "version": "8.11.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/enhanced-resolve": { + "version": "5.15.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/ent": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/errno": { + "version": "0.1.8", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.3", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", + "has-symbols": "^1.0.3", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.3", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.13", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.15" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es6-promise": { + "version": "4.2.8", + "dev": true, + "license": "MIT" + }, + "node_modules/es6-promisify": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promise": "^4.0.3" + } + }, + "node_modules/esbuild": { + "version": "0.18.17", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.17", + "@esbuild/android-arm64": "0.18.17", + "@esbuild/android-x64": "0.18.17", + "@esbuild/darwin-arm64": "0.18.17", + "@esbuild/darwin-x64": "0.18.17", + "@esbuild/freebsd-arm64": "0.18.17", + "@esbuild/freebsd-x64": "0.18.17", + "@esbuild/linux-arm": "0.18.17", + "@esbuild/linux-arm64": "0.18.17", + "@esbuild/linux-ia32": "0.18.17", + "@esbuild/linux-loong64": "0.18.17", + "@esbuild/linux-mips64el": "0.18.17", + "@esbuild/linux-ppc64": "0.18.17", + "@esbuild/linux-riscv64": "0.18.17", + "@esbuild/linux-s390x": "0.18.17", + "@esbuild/linux-x64": "0.18.17", + "@esbuild/netbsd-x64": "0.18.17", + "@esbuild/openbsd-x64": "0.18.17", + "@esbuild/sunos-x64": "0.18.17", + "@esbuild/win32-arm64": "0.18.17", + "@esbuild/win32-ia32": "0.18.17", + "@esbuild/win32-x64": "0.18.17" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.18.17", + "dev": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "dev": true, + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/escodegen/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.29.1", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.7", + "array.prototype.findlastindex": "^1.2.3", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.8.0", + "hasown": "^2.0.0", + "is-core-module": "^2.13.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.7", + "object.groupby": "^1.0.1", + "object.values": "^1.1.7", + "semver": "^6.3.1", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-jsdoc": { + "version": "48.2.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.42.0", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.3.4", + "escape-string-regexp": "^4.0.0", + "esquery": "^1.5.0", + "is-builtin-module": "^3.2.1", + "semver": "^7.6.0", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/spdx-expression-parse": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/eslint-plugin-prefer-arrow": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=2.0.0" + } + }, + "node_modules/eslint-plugin-unused-imports": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-rule-composer": "^0.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "6 - 7", + "eslint": "8" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-rule-composer": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.24.0", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/type-fest": { + "version": "0.20.2", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter-asyncresource": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/events": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.1", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/express": { + "version": "4.19.2", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.16.0", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/faye-websocket": { + "version": "0.11.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "websocket-driver": ">=0.5.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/figures": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-saver-es": { + "version": "2.0.5", + "license": "MIT" + }, + "node_modules/filelist": { + "version": "1.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "devOptional": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/find-cache-dir": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "common-path-prefix": "^3.0.0", + "pkg-dir": "^7.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/fs-monkey": { + "version": "1.0.5", + "dev": true, + "license": "Unlicense" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/glob": { + "version": "10.3.12", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "devOptional": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "13.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "dev": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/guess-parser": { + "version": "0.4.22", + "dev": true, + "license": "MIT", + "dependencies": { + "@wessberg/ts-evaluator": "0.0.27" + }, + "peerDependencies": { + "typescript": ">=3.7.5" + } + }, + "node_modules/hammerjs": { + "version": "2.0.8", + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/handle-thing": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/har-schema": { + "version": "2.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/har-validator/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/har-validator/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hdr-histogram-js": { + "version": "2.0.3", + "dev": true, + "license": "BSD", + "dependencies": { + "@assemblyscript/loader": "^0.10.1", + "base64-js": "^1.2.0", + "pako": "^1.0.3" + } + }, + "node_modules/hdr-histogram-percentiles-obj": { + "version": "3.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/hosted-git-info": { + "version": "6.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "node_modules/hpack.js/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/hpack.js/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/hpack.js/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^1.0.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-entities": { + "version": "2.5.2", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "dev": true, + "license": "MIT" + }, + "node_modules/http-proxy": { + "version": "1.18.1", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-signature": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-walk": { + "version": "6.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ignore-walk/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "9.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "dev": true, + "license": "MIT", + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "dev": true, + "license": "MIT" + }, + "node_modules/immutable": { + "version": "4.3.5", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "license": "ISC" + }, + "node_modules/ini": { + "version": "4.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/rxjs": { + "version": "7.8.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/inquirer/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/internal-slot": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ionicons": { + "version": "6.1.3", + "license": "MIT", + "dependencies": { + "@stencil/core": "^2.18.0" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ip-address/node_modules/jsbn": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/ipaddr.js": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "devOptional": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-builtin-module": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "builtin-modules": "^3.3.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-cwd": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-in-cwd": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-in-cwd/node_modules/is-path-inside": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-regex": { + "version": "1.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.13", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-what": { + "version": "3.14.1", + "dev": true, + "license": "MIT" + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/isobject": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.8.7", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jake/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jasmine": { + "version": "2.8.0", + "dev": true, + "license": "MIT", + "dependencies": { + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, + "node_modules/jasmine-core": { + "version": "4.5.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jasmine-spec-reporter": { + "version": "7.0.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "colors": "1.4.0" + } + }, + "node_modules/jasmine/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jasmine/node_modules/jasmine-core": { + "version": "2.8.0", + "dev": true, + "license": "MIT" + }, + "node_modules/jasminewd2": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.9.x" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/jsdom": { + "version": "16.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.2.4", + "acorn-globals": "^6.0.0", + "cssom": "^0.4.4", + "cssstyle": "^2.3.0", + "data-urls": "^2.0.0", + "decimal.js": "^10.2.1", + "domexception": "^2.0.1", + "escodegen": "^2.0.0", + "form-data": "^3.0.0", + "html-encoding-sniffer": "^2.0.1", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.0", + "parse5": "6.0.1", + "saxes": "^5.0.1", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.0.0", + "w3c-hr-time": "^1.0.2", + "w3c-xmlserializer": "^2.0.0", + "webidl-conversions": "^6.1.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^8.5.0", + "ws": "^7.4.6", + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "dev": true, + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/json5": { + "version": "2.2.3", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/jsprim": { + "version": "1.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/karma": { + "version": "6.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@colors/colors": "1.5.0", + "body-parser": "^1.19.0", + "braces": "^3.0.2", + "chokidar": "^3.5.1", + "connect": "^3.7.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.1", + "glob": "^7.1.7", + "graceful-fs": "^4.2.6", + "http-proxy": "^1.18.1", + "isbinaryfile": "^4.0.8", + "lodash": "^4.17.21", + "log4js": "^6.4.1", + "mime": "^2.5.2", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.5", + "qjobs": "^1.2.0", + "range-parser": "^1.2.1", + "rimraf": "^3.0.2", + "socket.io": "^4.4.1", + "source-map": "^0.6.1", + "tmp": "^0.2.1", + "ua-parser-js": "^0.7.30", + "yargs": "^16.1.1" + }, + "bin": { + "karma": "bin/karma" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/karma-chrome-launcher": { + "version": "3.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "which": "^1.2.1" + } + }, + "node_modules/karma-chrome-launcher/node_modules/which": { + "version": "1.3.1", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/karma-coverage": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.1", + "istanbul-reports": "^3.0.5", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/karma-coverage-istanbul-reporter": { + "version": "3.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^3.0.6", + "istanbul-reports": "^3.0.2", + "minimatch": "^3.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/mattlewis92" + } + }, + "node_modules/karma-coverage-istanbul-reporter/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/karma-coverage-istanbul-reporter/node_modules/istanbul-lib-source-maps": { + "version": "3.0.6", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/karma-coverage-istanbul-reporter/node_modules/istanbul-lib-source-maps/node_modules/istanbul-lib-coverage": { + "version": "2.0.5", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6" + } + }, + "node_modules/karma-coverage-istanbul-reporter/node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/karma-coverage-istanbul-reporter/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma-jasmine": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "jasmine-core": "^4.1.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "karma": "^6.0.0" + } + }, + "node_modules/karma-jasmine-html-reporter": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "peerDependencies": { + "jasmine-core": "^4.0.0 || ^5.0.0", + "karma": "^6.0.0", + "karma-jasmine": "^5.0.0" + } + }, + "node_modules/karma-source-map-support": { + "version": "1.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map-support": "^0.5.5" + } + }, + "node_modules/karma/node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/karma/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/karma/node_modules/mime": { + "version": "2.6.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/karma/node_modules/mkdirp": { + "version": "0.5.6", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/karma/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/karma/node_modules/tmp": { + "version": "0.2.1", + "dev": true, + "license": "MIT", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/karma/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/karma/node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.6", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/launch-editor": { + "version": "2.6.1", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, + "node_modules/less": { + "version": "4.2.0", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.0" + }, + "bin": { + "lessc": "bin/lessc" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "make-dir": "^2.1.0", + "mime": "^1.4.1", + "needle": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "11.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "less": "^3.5.0 || ^4.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/less/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/license-webpack-plugin": { + "version": "4.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lie": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log4js": { + "version": "6.9.1", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/make-dir": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "dev": true, + "license": "ISC" + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/@npmcli/fs": { + "version": "2.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/cacache": { + "version": "16.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/make-fetch-happen/node_modules/glob": { + "version": "8.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/make-fetch-happen/node_modules/minimatch": { + "version": "5.1.6", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-fetch-happen/node_modules/ssri": { + "version": "9.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/unique-filename": { + "version": "2.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/unique-slug": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/media-typer": { + "version": "0.3.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memfs": { + "version": "3.5.3", + "dev": true, + "license": "Unlicense", + "dependencies": { + "fs-monkey": "^1.0.4" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.7.6", + "dev": true, + "license": "MIT", + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-fetch": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-fetch/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-json-stream": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-json-stream/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/minizlib": { + "version": "2.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/multicast-dns": { + "version": "7.2.5", + "dev": true, + "license": "MIT", + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "dev": true, + "license": "ISC" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "dev": true, + "license": "MIT" + }, + "node_modules/needle": { + "version": "3.3.1", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "sax": "^1.2.4" + }, + "bin": { + "needle": "bin/needle" + }, + "engines": { + "node": ">= 4.4.x" + } + }, + "node_modules/negotiator": { + "version": "0.6.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "dev": true, + "license": "MIT" + }, + "node_modules/ng2-charts": { + "version": "4.1.1", + "license": "ISC", + "dependencies": { + "lodash-es": "^4.17.15", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/cdk": ">=14.0.0", + "@angular/common": ">=14.0.0", + "@angular/core": ">=14.0.0", + "chart.js": "^3.4.0 || ^4.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/ngx-cookie-service": { + "version": "16.1.0", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": "^16.0.0", + "@angular/core": "^16.0.0" + } + }, + "node_modules/ngx-spinner": { + "version": "16.0.2", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/animations": ">=15.0.0", + "@angular/common": ">=15.0.0", + "@angular/core": ">=15.0.0" + } + }, + "node_modules/nice-napi": { + "version": "1.0.2", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "!win32" + ], + "dependencies": { + "node-addon-api": "^3.0.0", + "node-gyp-build": "^4.2.2" + } + }, + "node_modules/node-addon-api": { + "version": "3.2.1", + "dev": true, + "license": "MIT" + }, + "node_modules/node-forge": { + "version": "1.3.1", + "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "9.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.0", + "dev": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "dev": true, + "license": "MIT" + }, + "node_modules/nopt": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-package-data": { + "version": "5.0.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-install-checks": { + "version": "6.3.0", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-package-arg": { + "version": "10.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "8.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^10.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "14.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/npm-registry-fetch/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "11.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-registry-fetch/node_modules/minipass-fetch": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm-registry-fetch/node_modules/minipass-fetch/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.7", + "dev": true, + "license": "MIT" + }, + "node_modules/nx": { + "version": "16.5.1", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@nrwl/tao": "16.5.1", + "@parcel/watcher": "2.0.4", + "@yarnpkg/lockfile": "^1.1.0", + "@yarnpkg/parsers": "3.0.0-rc.46", + "@zkochan/js-yaml": "0.0.6", + "axios": "^1.0.0", + "chalk": "^4.1.0", + "cli-cursor": "3.1.0", + "cli-spinners": "2.6.1", + "cliui": "^7.0.2", + "dotenv": "~10.0.0", + "enquirer": "~2.3.6", + "fast-glob": "3.2.7", + "figures": "3.2.0", + "flat": "^5.0.2", + "fs-extra": "^11.1.0", + "glob": "7.1.4", + "ignore": "^5.0.4", + "js-yaml": "4.1.0", + "jsonc-parser": "3.2.0", + "lines-and-columns": "~2.0.3", + "minimatch": "3.0.5", + "npm-run-path": "^4.0.1", + "open": "^8.4.0", + "semver": "7.5.3", + "string-width": "^4.2.3", + "strong-log-transformer": "^2.1.0", + "tar-stream": "~2.2.0", + "tmp": "~0.2.1", + "tsconfig-paths": "^4.1.2", + "tslib": "^2.3.0", + "v8-compile-cache": "2.3.0", + "yargs": "^17.6.2", + "yargs-parser": "21.1.1" + }, + "bin": { + "nx": "bin/nx.js" + }, + "optionalDependencies": { + "@nx/nx-darwin-arm64": "16.5.1", + "@nx/nx-darwin-x64": "16.5.1", + "@nx/nx-freebsd-x64": "16.5.1", + "@nx/nx-linux-arm-gnueabihf": "16.5.1", + "@nx/nx-linux-arm64-gnu": "16.5.1", + "@nx/nx-linux-arm64-musl": "16.5.1", + "@nx/nx-linux-x64-gnu": "16.5.1", + "@nx/nx-linux-x64-musl": "16.5.1", + "@nx/nx-win32-arm64-msvc": "16.5.1", + "@nx/nx-win32-x64-msvc": "16.5.1" + }, + "peerDependencies": { + "@swc-node/register": "^1.4.2", + "@swc/core": "^1.2.173" + }, + "peerDependenciesMeta": { + "@swc-node/register": { + "optional": true + }, + "@swc/core": { + "optional": true + } + } + }, + "node_modules/nx/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/nx/node_modules/argparse": { + "version": "2.0.1", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/nx/node_modules/chalk": { + "version": "4.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/nx/node_modules/cli-spinners": { + "version": "2.6.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/nx/node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/nx/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/nx/node_modules/fast-glob": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nx/node_modules/fs-extra": { + "version": "11.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/nx/node_modules/glob": { + "version": "7.1.4", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nx/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/nx/node_modules/js-yaml": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/nx/node_modules/jsonc-parser": { + "version": "3.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/nx/node_modules/jsonfile": { + "version": "6.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/nx/node_modules/lines-and-columns": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/nx/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nx/node_modules/minimatch": { + "version": "3.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nx/node_modules/semver": { + "version": "7.5.3", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nx/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/nx/node_modules/tmp": { + "version": "0.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/nx/node_modules/tsconfig-paths": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "json5": "^2.2.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/nx/node_modules/universalify": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/nx/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-path": { + "version": "0.11.8", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obuf": { + "version": "1.1.2", + "dev": true, + "license": "MIT" + }, + "node_modules/on-finished": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/open": { + "version": "8.4.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "dev": true, + "license": "MIT", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "4.3.0", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/color-convert": { + "version": "2.0.1", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/node_modules/has-flag": { + "version": "4.0.0", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "7.2.0", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pacote": { + "version": "15.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^4.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^5.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^1.3.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-node-version": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/parse5": { + "version": "6.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5-html-rewriting-stream": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.3.0", + "parse5": "^7.0.0", + "parse5-sax-parser": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-html-rewriting-stream/node_modules/parse5": { + "version": "7.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-sax-parser": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-sax-parser/node_modules/parse5": { + "version": "7.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-is-inside": { + "version": "1.0.2", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/path-key": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.10.2", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "dev": true, + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/piscina": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "eventemitter-asyncresource": "^1.0.0", + "hdr-histogram-js": "^2.0.1", + "hdr-histogram-percentiles-obj": "^3.0.0" + }, + "optionalDependencies": { + "nice-napi": "^1.0.2" + } + }, + "node_modules/pkg-dir": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^6.3.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "6.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/pkg-dir/node_modules/yocto-queue": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.35", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-loader": { + "version": "7.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^8.2.0", + "jiti": "^1.18.2", + "semver": "^7.3.8" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "postcss": "^7.0.0 || ^8.0.1", + "webpack": "^5.0.0" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.1.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.2.0", + "dev": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.16", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-bytes": { + "version": "5.6.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/proc-log": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/protractor": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", + "jasmine": "2.8.0", + "jasminewd2": "^2.1.0", + "q": "1.4.1", + "saucelabs": "^1.5.0", + "selenium-webdriver": "3.6.0", + "source-map-support": "~0.4.0", + "webdriver-js-extender": "2.1.0", + "webdriver-manager": "^12.1.7", + "yargs": "^15.3.1" + }, + "bin": { + "protractor": "bin/protractor", + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=10.13.x" + } + }, + "node_modules/protractor/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/ansi-styles": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/chalk": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/cliui": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/protractor/node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/protractor/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/protractor/node_modules/source-map": { + "version": "0.5.7", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/source-map-support": { + "version": "0.4.18", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "^0.5.6" + } + }, + "node_modules/protractor/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/protractor/node_modules/supports-color": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/protractor/node_modules/wrap-ansi": { + "version": "6.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "5.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/protractor/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/y18n": { + "version": "4.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/protractor/node_modules/yargs": { + "version": "15.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/protractor/node_modules/yargs-parser": { + "version": "18.1.3", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-addr/node_modules/ipaddr.js": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/prr": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/psl": { + "version": "1.9.0", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/q": { + "version": "1.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, + "node_modules/qjobs": { + "version": "1.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.9" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/randombytes": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-package-json": { + "version": "6.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.0", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "devOptional": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect-metadata": { + "version": "0.1.14", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/regenerate": { + "version": "1.4.2", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.1.1", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "license": "MIT" + }, + "node_modules/regenerator-transform": { + "version": "0.15.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "node_modules/regex-parser": { + "version": "2.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "5.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/regjsgen": "^0.8.0", + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.1.0", + "regjsparser": "^0.9.1", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsparser": { + "version": "0.9.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~0.5.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/regjsparser/node_modules/jsesc": { + "version": "0.5.0", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + } + }, + "node_modules/request": { + "version": "2.88.2", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.8", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-url-loader": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/resolve-url-loader/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/rimraf": { + "version": "3.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roboto-fontface": { + "version": "0.10.0", + "license": "Apache-2.0" + }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "license": "Unlicense" + }, + "node_modules/rollup": { + "version": "3.29.4", + "dev": true, + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "license": "BSD-3-Clause" + }, + "node_modules/rxjs": { + "version": "6.6.7", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/rxjs/node_modules/tslib": { + "version": "1.14.1", + "license": "0BSD" + }, + "node_modules/safe-array-concat": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-regex": "^1.1.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.64.1", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/sass-loader": { + "version": "13.3.2", + "dev": true, + "license": "MIT", + "dependencies": { + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/saucelabs": { + "version": "1.5.0", + "dev": true, + "dependencies": { + "https-proxy-agent": "^2.2.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/saucelabs/node_modules/agent-base": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "es6-promisify": "^5.0.0" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/saucelabs/node_modules/debug": { + "version": "3.2.7", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/saucelabs/node_modules/https-proxy-agent": { + "version": "2.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/sax": { + "version": "1.3.0", + "dev": true, + "license": "ISC" + }, + "node_modules/saxes": { + "version": "5.0.1", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/schema-utils": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/selenium-webdriver": { + "version": "3.6.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jszip": "^3.1.3", + "rimraf": "^2.5.4", + "tmp": "0.0.30", + "xml2js": "^0.4.17" + }, + "engines": { + "node": ">= 6.9.0" + } + }, + "node_modules/selenium-webdriver/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/selenium-webdriver/node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/selenium-webdriver/node_modules/tmp": { + "version": "0.0.30", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.1" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/selfsigned": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node-forge": "^1.3.0", + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.6.0", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/send": { + "version": "0.18.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "dev": true, + "license": "MIT" + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/serve-index": { + "version": "1.9.1", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.6.3", + "dev": true, + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/inherits": { + "version": "2.0.3", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "dev": true, + "license": "MIT" + }, + "node_modules/serve-index/node_modules/setprototypeof": { + "version": "1.1.0", + "dev": true, + "license": "ISC" + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "dev": true, + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "dev": true, + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "dev": true, + "license": "ISC" + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.1", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "license": "ISC" + }, + "node_modules/sigstore": { + "version": "1.9.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "@sigstore/sign": "^1.0.0", + "@sigstore/tuf": "^1.0.3", + "make-fetch-happen": "^11.0.1" + }, + "bin": { + "sigstore": "bin/sigstore.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/sigstore/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sigstore/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/sigstore/node_modules/make-fetch-happen": { + "version": "11.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/minipass-fetch": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/sigstore/node_modules/minipass-fetch/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.7.3", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-adapter/node_modules/ws": { + "version": "8.11.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sockjs": { + "version": "0.3.24", + "dev": true, + "license": "MIT", + "dependencies": { + "faye-websocket": "^0.11.3", + "uuid": "^8.3.2", + "websocket-driver": "^0.7.4" + } + }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/socks": { + "version": "2.8.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-loader": { + "version": "4.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "license": "MIT" + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.16", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/spdy": { + "version": "4.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/spdy-transport": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/sshpk": { + "version": "1.18.0", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "10.0.5", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamroller": { + "version": "3.1.5", + "dev": true, + "license": "MIT", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.9", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strong-log-transformer": { + "version": "2.1.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "duplexer": "^0.1.1", + "minimist": "^1.2.0", + "through": "^2.3.4" + }, + "bin": { + "sl-log-transformer": "bin/sl-log-transformer.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swiper": { + "version": "11.1.1", + "funding": [ + { + "type": "patreon", + "url": "https://www.patreon.com/swiperjs" + }, + { + "type": "open_collective", + "url": "http://opencollective.com/swiper" + } + ], + "license": "MIT", + "engines": { + "node": ">= 4.7.0" + } + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "dev": true, + "license": "ISC" + }, + "node_modules/terser": { + "version": "5.29.1", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "3.5.2", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT" + }, + "node_modules/terser-webpack-plugin/node_modules/schema-utils": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "dev": true, + "license": "MIT" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/through": { + "version": "2.3.8", + "dev": true, + "license": "MIT" + }, + "node_modules/thunky": { + "version": "1.1.0", + "dev": true, + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.0.33", + "dev": true, + "license": "MIT", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "devOptional": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.3", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tree-kill": { + "version": "1.2.2", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "license": "0BSD" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "dev": true, + "license": "0BSD" + }, + "node_modules/tuf-js": { + "version": "1.1.7", + "dev": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "1.0.4", + "debug": "^4.3.4", + "make-fetch-happen": "^11.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/@tootallnate/once": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/tuf-js/node_modules/http-proxy-agent": { + "version": "5.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tuf-js/node_modules/lru-cache": { + "version": "7.18.3", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/tuf-js/node_modules/make-fetch-happen": { + "version": "11.1.1", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^5.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/minipass-fetch": { + "version": "3.0.4", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/tuf-js/node_modules/minipass-fetch/node_modules/minipass": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "dev": true, + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.6", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-assert": { + "version": "1.0.9", + "dev": true, + "license": "MIT" + }, + "node_modules/typescript": { + "version": "4.9.5", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typescript-strict-plugin": { + "version": "2.4.1", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^3.0.0", + "execa": "^4.0.0", + "minimatch": "^9.0.3", + "ora": "^5.4.1", + "yargs": "^16.2.0" + }, + "bin": { + "tsc-strict": "dist/cli/tsc-strict/index.js", + "update-strict-comments": "dist/cli/update-strict-comments/index.js" + } + }, + "node_modules/typescript-strict-plugin/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/typescript-strict-plugin/node_modules/brace-expansion": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/typescript-strict-plugin/node_modules/chalk": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript-strict-plugin/node_modules/cliui": { + "version": "7.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/typescript-strict-plugin/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/typescript-strict-plugin/node_modules/execa": { + "version": "4.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/typescript-strict-plugin/node_modules/get-stream": { + "version": "5.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript-strict-plugin/node_modules/has-flag": { + "version": "4.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript-strict-plugin/node_modules/human-signals": { + "version": "1.1.1", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/typescript-strict-plugin/node_modules/minimatch": { + "version": "9.0.4", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typescript-strict-plugin/node_modules/supports-color": { + "version": "7.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/typescript-strict-plugin/node_modules/yargs": { + "version": "16.2.0", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typescript-strict-plugin/node_modules/yargs-parser": { + "version": "20.2.9", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ua-parser-js": { + "version": "0.7.37", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + }, + { + "type": "github", + "url": "https://github.com/sponsors/faisalman" + } + ], + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "dev": true, + "license": "MIT" + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-filename": { + "version": "3.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/unique-slug": { + "version": "4.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "9.0.1", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "5.0.0", + "dev": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vite": { + "version": "4.5.2", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/void-elements": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/w3c-hr-time": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "browser-process-hrtime": "^1.0.0" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/wbuf": { + "version": "1.7.3", + "dev": true, + "license": "MIT", + "dependencies": { + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webdriver-js-extender": { + "version": "2.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager": { + "version": "12.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "adm-zip": "^0.5.2", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" + }, + "bin": { + "webdriver-manager": "bin/webdriver-manager" + }, + "engines": { + "node": ">=6.9.x" + } + }, + "node_modules/webdriver-manager/node_modules/ansi-regex": { + "version": "2.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/ansi-styles": { + "version": "2.2.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/chalk": { + "version": "1.1.3", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/glob": { + "version": "7.2.3", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/webdriver-manager/node_modules/ini": { + "version": "1.3.8", + "dev": true, + "license": "ISC" + }, + "node_modules/webdriver-manager/node_modules/rimraf": { + "version": "2.7.1", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/webdriver-manager/node_modules/semver": { + "version": "5.7.2", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/webdriver-manager/node_modules/strip-ansi": { + "version": "3.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/webdriver-manager/node_modules/supports-color": { + "version": "2.0.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/webidl-conversions": { + "version": "6.1.0", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=10.4" + } + }, + "node_modules/webpack": { + "version": "5.90.3", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-middleware": { + "version": "6.1.2", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.12", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server": { + "version": "4.15.1", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.5", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { + "version": "5.3.4", + "dev": true, + "license": "MIT", + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/webpack-dev-server/node_modules/ws": { + "version": "8.16.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/webpack-merge": { + "version": "5.9.0", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack-subresource-integrity": { + "version": "5.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/node_modules/ajv": { + "version": "6.12.6", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "3.5.2", + "dev": true, + "license": "MIT", + "peer": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "0.4.1", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/webpack/node_modules/schema-utils": { + "version": "3.3.0", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/websocket-driver": { + "version": "0.7.4", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "http-parser-js": ">=0.5.1", + "safe-buffer": ">=5.1.0", + "websocket-extensions": ">=0.1.1" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/websocket-extensions": { + "version": "0.1.4", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "1.0.5", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.4.24" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.4.24", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "2.3.0", + "dev": true, + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "8.7.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash": "^4.7.0", + "tr46": "^2.1.0", + "webidl-conversions": "^6.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/which": { + "version": "2.0.2", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "dev": true, + "license": "ISC" + }, + "node_modules/which-typed-array": { + "version": "1.1.15", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.1", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "dev": true, + "license": "ISC" + }, + "node_modules/ws": { + "version": "7.5.9", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true } + } + }, + "node_modules/xml-name-validator": { + "version": "3.0.0", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/xml2js": { + "version": "0.4.23", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zone.js": { + "version": "0.13.3", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + } } + } } diff --git a/ui/package.json b/ui/package.json index 9b6c856ed55..6d98a3e50e9 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,77 +1,78 @@ { - "name": "openems-ui", - "version": "2024.4.0", - "license": "AGPL-3.0", - "private": true, - "dependencies": { - "@angular/animations": "~15.2.9", - "@angular/cdk": "~15.2.9", - "@angular/common": "~15.2.9", - "@angular/core": "~15.2.9", - "@angular/forms": "~15.2.9", - "@angular/platform-browser": "~15.2.9", - "@angular/platform-browser-dynamic": "~15.2.9", - "@angular/router": "~15.2.9", - "@angular/service-worker": "~15.2.9", - "@ionic/angular": "^6.7.4", - "@ngx-formly/core": "^6.1.7", - "@ngx-formly/ionic": "^6.1.7", - "@ngx-formly/schematics": "^6.1.7", - "@ngx-translate/core": "^14.0.0", - "angular-mydatepicker": "^0.11.5", - "chart.js": "^4.4.0", - "chartjs-adapter-date-fns": "^3.0.0", - "chartjs-plugin-zoom": "^2.0.1", - "classlist.js": "^1.1.20150312", - "compare-versions": "^6.1.0", - "d3": "^7.9.0", - "date-fns": "^2.30.0", - "file-saver-es": "^2.0.5", - "ng2-charts": "4.1.1", - "ngx-cookie-service": "^15.0.0", - "ngx-spinner": "^15.0.1", - "roboto-fontface": "^0.10.0", - "rxjs": "~6.6.7", - "tslib": "^2.5.0", - "uuid": "^9.0.0", - "zone.js": "~0.13.0" - }, - "devDependencies": { - "@angular-devkit/build-angular": "^15.2.8", - "@angular-eslint/builder": "~15.2.1", - "@angular-eslint/eslint-plugin": "~15.2.1", - "@angular-eslint/eslint-plugin-template": "~15.2.1", - "@angular-eslint/template-parser": "~15.2.1", - "@angular/cli": "~15.2.8", - "@angular/compiler": "~15.2.9", - "@angular/compiler-cli": "~15.2.9", - "@angular/language-service": "~15.2.9", - "@ionic/angular-toolkit": "^7.0.0", - "@types/jasmine": "~4.3.2", - "@types/jasminewd2": "~2.0.10", - "@types/node": "^20.2.5", - "@types/uuid": "^9.0.2", - "@typescript-eslint/eslint-plugin": "^6.2.0", - "@typescript-eslint/parser": "6.2.0", - "eslint": "^8.41.0", - "eslint-plugin-import": "2.27.5", - "eslint-plugin-jsdoc": "45.0.0", - "eslint-plugin-prefer-arrow": "1.2.3", - "eslint-plugin-unused-imports": "^3.1.0", - "jasmine-core": "~4.5.0", - "jasmine-spec-reporter": "~7.0.0", - "karma": "~6.4.2", - "karma-chrome-launcher": "~3.2.0", - "karma-coverage": "~2.2.0", - "karma-coverage-istanbul-reporter": "~3.0.3", - "karma-jasmine": "~5.1.0", - "karma-jasmine-html-reporter": "^2.0.0", - "protractor": "~7.0.0", - "ts-node": "~10.9.1", - "typescript": "~4.9.5" - }, - "scripts": { - "lint": "ng lint", - "test": "ng test" - } + "name": "openems-ui", + "version": "2024.5.0", + "license": "AGPL-3.0", + "private": true, + "dependencies": { + "@angular/animations": "~16.2.12", + "@angular/common": "~16.2.12", + "@angular/core": "~16.2.12", + "@angular/forms": "~16.2.12", + "@angular/platform-browser": "~16.2.12", + "@angular/platform-browser-dynamic": "~16.2.12", + "@angular/router": "~16.2.12", + "@angular/service-worker": "~16.2.12", + "@ionic/angular": "^6.7.5", + "@ngx-formly/core": "^6.3.0", + "@ngx-formly/ionic": "^6.3.0", + "@ngx-formly/schematics": "^6.3.0", + "@ngx-translate/core": "^14.0.0", + "@nodro7/angular-mydatepicker": "^0.14.0", + "chart.js": "^4.4.2", + "chartjs-adapter-date-fns": "^3.0.0", + "chartjs-plugin-zoom": "^2.0.1", + "classlist.js": "^1.1.20150312", + "compare-versions": "^6.1.0", + "d3": "^7.9.0", + "date-fns": "^2.30.0", + "file-saver-es": "^2.0.5", + "ng2-charts": "4.1.1", + "ngx-cookie-service": "^16.1.0", + "ngx-spinner": "^16.0.2", + "roboto-fontface": "^0.10.0", + "rxjs": "~6.6.7", + "swiper": "11.1.1", + "tslib": "^2.6.2", + "uuid": "^9.0.1", + "zone.js": "~0.13.3" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^16.2.13", + "@angular-eslint/builder": "^16.3.1", + "@angular-eslint/eslint-plugin": "^16.3.1", + "@angular-eslint/eslint-plugin-template": "^16.3.1", + "@angular-eslint/template-parser": "^16.3.1", + "@angular/cli": "^16.2.13", + "@angular/compiler": "^16.2.12", + "@angular/compiler-cli": "^16.2.12", + "@angular/language-service": "^16.2.12", + "@ionic/angular-toolkit": "^11.0.1", + "@types/jasmine": "~4.3.6", + "@types/jasminewd2": "~2.0.13", + "@types/node": "^20.12.6", + "@types/uuid": "^9.0.8", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint": "^8.57.0", + "eslint-plugin-import": "2.29.1", + "eslint-plugin-jsdoc": "48.2.3", + "eslint-plugin-prefer-arrow": "1.2.3", + "eslint-plugin-unused-imports": "^3.1.0", + "jasmine-core": "~4.5.0", + "jasmine-spec-reporter": "~7.0.0", + "karma": "~6.4.2", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.1", + "karma-coverage-istanbul-reporter": "~3.0.3", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "^2.1.0", + "protractor": "~7.0.0", + "ts-node": "^10.9.2", + "typescript": "~4.9.5", + "typescript-strict-plugin": "^2.4.1" + }, + "scripts": { + "lint": "ng lint", + "test": "ng test" + } } diff --git a/ui/src/app/app-routing.module.ts b/ui/src/app/app-routing.module.ts index dfc0e25f4e5..e1f9f03e9b3 100644 --- a/ui/src/app/app-routing.module.ts +++ b/ui/src/app/app-routing.module.ts @@ -36,6 +36,7 @@ import { IndexComponent as EdgeSettingsComponentInstallIndexComponentComponent } import { ComponentInstallComponent as EdgeSettingsComponentInstallComponentComponent } from './edge/settings/component/install/install.component'; import { IndexComponent as EdgeSettingsComponentUpdateIndexComponentComponent } from './edge/settings/component/update/index.component'; import { ComponentUpdateComponent as EdgeSettingsComponentUpdateComponentComponent } from './edge/settings/component/update/update.component'; +import { JsonrpcTestComponent } from './edge/settings/jsonrpctest/jsonrpctest'; import { NetworkComponent as EdgeSettingsNetworkComponent } from './edge/settings/network/network.component'; import { AliasUpdateComponent } from './edge/settings/profile/aliasupdate.component'; import { ProfileComponent as EdgeSettingsProfileComponent } from './edge/settings/profile/profile.component'; @@ -116,6 +117,7 @@ const routes: Routes = [ { path: 'settings/app/update/:appId', component: EdgeSettingsAppUpdate }, { path: 'settings/app/single/:appId', component: EdgeSettingsAppSingle }, { path: 'settings/alerting', component: EdgeSettingsAlerting }, + { path: 'settings/jsonrpctest', component: JsonrpcTestComponent }, ], }, diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html index 0fed6720b56..80c7aa7d2a0 100644 --- a/ui/src/app/app.component.html +++ b/ui/src/app/app.component.html @@ -78,5 +78,5 @@

    Index.connectionFailed

    - + diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index db0bc53c50d..bcf853fe001 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -1,7 +1,8 @@ +// @ts-strict-ignore import { Component, OnDestroy, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; +import { NavigationEnd, Router } from '@angular/router'; import { MenuController, ModalController, Platform, ToastController } from '@ionic/angular'; -import { Subject } from 'rxjs'; +import { Subject, Subscription } from 'rxjs'; import { filter, takeUntil } from 'rxjs/operators'; import { Meta } from '@angular/platform-browser'; @@ -21,9 +22,11 @@ export class AppComponent implements OnInit, OnDestroy { public enableSideMenu: boolean; public isSystemLogEnabled: boolean = false; private ngUnsubscribe: Subject = new Subject(); + private subscription: Subscription = new Subscription(); protected isUserAllowedToSeeOverview: boolean = false; protected isUserAllowedToSeeFooter: boolean = false; + protected isHistoryDetailView: boolean = false; constructor( private platform: Platform, @@ -38,10 +41,19 @@ export class AppComponent implements OnInit, OnDestroy { ) { service.setLang(Language.getByKey(localStorage.LANGUAGE) ?? Language.getByBrowserLang(navigator.language)); - this.service.metadata.pipe(filter(metadata => !!metadata)).subscribe(metadata => { - this.isUserAllowedToSeeOverview = UserPermission.isUserAllowedToSeeOverview(metadata.user); - this.isUserAllowedToSeeFooter = UserPermission.isUserAllowedToSeeFooter(metadata.user); - }); + + this.subscription.add( + this.service.metadata.pipe(filter(metadata => !!metadata)).subscribe(metadata => { + this.isUserAllowedToSeeOverview = UserPermission.isUserAllowedToSeeOverview(metadata.user); + this.isUserAllowedToSeeFooter = UserPermission.isUserAllowedToSeeFooter(metadata.user); + })); + + this.subscription.add( + this.router.events.pipe(filter(event => event instanceof NavigationEnd)).subscribe((e: NavigationEnd) => { + // Hide footer for history detail views + const segments = e.url.split('/'); + this.isHistoryDetailView = segments.slice(0, -1).includes('history'); + })); } ngOnInit() { @@ -107,5 +119,6 @@ export class AppComponent implements OnInit, OnDestroy { ngOnDestroy() { this.ngUnsubscribe.next(); this.ngUnsubscribe.complete(); + this.subscription.unsubscribe(); } } diff --git a/ui/src/app/app.module.ts b/ui/src/app/app.module.ts index 7113abdf1f7..97e37811918 100644 --- a/ui/src/app/app.module.ts +++ b/ui/src/app/app.module.ts @@ -8,7 +8,6 @@ import { RouteReuseStrategy } from '@angular/router'; import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; import { FORMLY_CONFIG } from '@ngx-formly/core'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; -import { AngularMyDatePickerModule } from 'angular-mydatepicker'; import { CookieService } from 'ngx-cookie-service'; import { AppRoutingModule } from './app-routing.module'; @@ -29,6 +28,7 @@ import { StatusSingleComponent } from './shared/status/single/status.component'; import { registerTranslateExtension } from './shared/translate.extension'; import { Language, MyTranslateLoader } from './shared/type/language'; import { UserModule } from './user/user.module'; +import { AngularMyDatePickerModule } from '@nodro7/angular-mydatepicker'; @NgModule({ declarations: [ @@ -38,10 +38,6 @@ import { UserModule } from './user/user.module'; StatusSingleComponent, SystemLogComponent, ], - entryComponents: [ - ChartOptionsPopoverComponent, - PickDatePopoverComponent, - ], imports: [ AngularMyDatePickerModule, AppRoutingModule, diff --git a/ui/src/app/changelog/view/component/changelog.constants.ts b/ui/src/app/changelog/view/component/changelog.constants.ts index 0e63559e85b..fd9fa77b986 100644 --- a/ui/src/app/changelog/view/component/changelog.constants.ts +++ b/ui/src/app/changelog/view/component/changelog.constants.ts @@ -2,7 +2,7 @@ import { Role } from "src/app/shared/type/role"; export class Changelog { - public static readonly UI_VERSION = "2024.4.0"; + public static readonly UI_VERSION = "2024.5.0"; public static product(...products: Product[]) { return products.map(product => Changelog.link(product.name, product.url)).join(", ") + '. '; diff --git a/ui/src/app/edge/edge.component.ts b/ui/src/app/edge/edge.component.ts index 90d88be7e0a..9e4d158846c 100644 --- a/ui/src/app/edge/edge.component.ts +++ b/ui/src/app/edge/edge.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { SubscribeEdgesRequest } from "src/app/shared/jsonrpc/request/subscribeEdgesRequest"; diff --git a/ui/src/app/edge/history/Controller/ChannelThreshold/channelThreshold.module.ts b/ui/src/app/edge/history/Controller/ChannelThreshold/channelThreshold.module.ts index 91953858eff..bf6f9d53acf 100644 --- a/ui/src/app/edge/history/Controller/ChannelThreshold/channelThreshold.module.ts +++ b/ui/src/app/edge/history/Controller/ChannelThreshold/channelThreshold.module.ts @@ -10,9 +10,6 @@ import { OverviewComponent } from "./overview/overview"; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ], declarations: [ FlatComponent, TotalChartComponent, diff --git a/ui/src/app/edge/history/Controller/ChannelThreshold/chart/totalchart.component.ts b/ui/src/app/edge/history/Controller/ChannelThreshold/chart/totalchart.component.ts index 711bf37fb0c..14d525d0c37 100644 --- a/ui/src/app/edge/history/Controller/ChannelThreshold/chart/totalchart.component.ts +++ b/ui/src/app/edge/history/Controller/ChannelThreshold/chart/totalchart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; import { QueryHistoricTimeseriesEnergyResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesEnergyResponse'; diff --git a/ui/src/app/edge/history/Controller/ChannelThreshold/flat/flat.ts b/ui/src/app/edge/history/Controller/ChannelThreshold/flat/flat.ts index a6d91689197..d46cae5a51f 100644 --- a/ui/src/app/edge/history/Controller/ChannelThreshold/flat/flat.ts +++ b/ui/src/app/edge/history/Controller/ChannelThreshold/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/chart/chart.ts b/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/chart/chart.ts index e51d62b69b9..84ad2eb878e 100644 --- a/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/chart/chart.ts +++ b/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/chart/chart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from '@angular/core'; import * as Chart from 'chart.js'; import { calculateResolution, ChronoUnit, Resolution } from 'src/app/edge/history/shared'; @@ -14,7 +15,6 @@ export class ChartComponent extends AbstractHistoryChart { @Input() public override component: EdgeConfig.Component; - private TimeOfUseTariffState = TimeOfUseTariffUtils.TimeOfUseTariffState; private currencyLabel: Currency.Label; // Default protected override getChartData(): HistoryUtils.ChartData { @@ -49,21 +49,21 @@ export class ChartComponent extends AbstractHistoryChart { output: (data: HistoryUtils.ChannelData) => { return [{ name: this.translate.instant('Edge.Index.Widgets.TIME_OF_USE_TARIFF.STATE.BALANCING'), - converter: () => this.getDataset(data, this.TimeOfUseTariffState.Balancing), + converter: () => this.getDataset(data, TimeOfUseTariffUtils.State.Balancing), color: 'rgb(51,102,0)', stack: 1, order: 1, }, { name: this.translate.instant('Edge.Index.Widgets.TIME_OF_USE_TARIFF.STATE.CHARGE_GRID'), - converter: () => this.getDataset(data, this.TimeOfUseTariffState.ChargeGrid), + converter: () => this.getDataset(data, TimeOfUseTariffUtils.State.ChargeGrid), color: 'rgb(0, 204, 204)', stack: 1, order: 1, }, { name: this.translate.instant('Edge.Index.Widgets.TIME_OF_USE_TARIFF.STATE.DELAY_DISCHARGE'), - converter: () => this.getDataset(data, this.TimeOfUseTariffState.DelayDischarge), + converter: () => this.getDataset(data, TimeOfUseTariffUtils.State.DelayDischarge), color: 'rgb(0,0,0)', stack: 1, order: 1, diff --git a/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/flat/flat.ts b/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/flat/flat.ts index ea86a49ea0b..eeb4fb4fab2 100644 --- a/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/flat/flat.ts +++ b/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/timeOfUseTariff.module.ts b/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/timeOfUseTariff.module.ts index 6ad0ccbfb00..16ac1004777 100644 --- a/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/timeOfUseTariff.module.ts +++ b/ui/src/app/edge/history/Controller/Ess/TimeOfUseTariff/timeOfUseTariff.module.ts @@ -10,9 +10,6 @@ import { ChartComponent } from "./chart/chart"; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ], declarations: [ FlatComponent, OverviewComponent, diff --git a/ui/src/app/edge/history/abstracthistorychart.ts b/ui/src/app/edge/history/abstracthistorychart.ts index d0b96867714..b225e292d7c 100644 --- a/ui/src/app/edge/history/abstracthistorychart.ts +++ b/ui/src/app/edge/history/abstracthistorychart.ts @@ -1,7 +1,7 @@ +// @ts-strict-ignore import { TranslateService } from '@ngx-translate/core'; import * as Chart from 'chart.js'; import { AbstractHistoryChart as NewAbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; -import { ChartConstants } from 'src/app/shared/genericComponents/chart/chart.constants'; import { JsonrpcResponseError } from 'src/app/shared/jsonrpc/base'; import { QueryHistoricTimeseriesDataRequest } from "src/app/shared/jsonrpc/request/queryHistoricTimeseriesDataRequest"; import { QueryHistoricTimeseriesEnergyPerPeriodRequest } from 'src/app/shared/jsonrpc/request/queryHistoricTimeseriesEnergyPerPeriodRequest'; @@ -412,9 +412,8 @@ export abstract class AbstractHistoryChart { break; } - const scaleOptions: { min: number, max: number, stepSize: number } | null = ChartConstants.getScaleOptions(this.datasets, yAxis); // Only one yAxis defined - options = NewAbstractHistoryChart.getYAxisOptions(options, yAxis, this.translate, 'line', locale, false, scaleOptions); + options = NewAbstractHistoryChart.getYAxisOptions(options, yAxis, this.translate, 'line', locale, false); options.scales.x['stacked'] = true; options.scales[ChartAxis.LEFT]['stacked'] = false; diff --git a/ui/src/app/edge/history/abstracthistorywidget.ts b/ui/src/app/edge/history/abstracthistorywidget.ts index 84b241264cd..8a0e419c4c4 100644 --- a/ui/src/app/edge/history/abstracthistorywidget.ts +++ b/ui/src/app/edge/history/abstracthistorywidget.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { JsonrpcResponseError } from 'src/app/shared/jsonrpc/base'; import { QueryHistoricTimeseriesDataRequest } from 'src/app/shared/jsonrpc/request/queryHistoricTimeseriesDataRequest'; import { QueryHistoricTimeseriesDataResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesDataResponse'; diff --git a/ui/src/app/edge/history/chpsoc/chart.component.ts b/ui/src/app/edge/history/chpsoc/chart.component.ts index b825b5d31f0..aa37afaee2f 100644 --- a/ui/src/app/edge/history/chpsoc/chart.component.ts +++ b/ui/src/app/edge/history/chpsoc/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/chpsoc/chpsocchartoverview/chpsocchartoverview.component.ts b/ui/src/app/edge/history/chpsoc/chpsocchartoverview/chpsocchartoverview.component.ts index c55484dfa5f..e5b740eccec 100644 --- a/ui/src/app/edge/history/chpsoc/chpsocchartoverview/chpsocchartoverview.component.ts +++ b/ui/src/app/edge/history/chpsoc/chpsocchartoverview/chpsocchartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service } from '../../../../shared/shared'; diff --git a/ui/src/app/edge/history/chpsoc/widget.component.html b/ui/src/app/edge/history/chpsoc/widget.component.html index 52a74471a19..5c3e6d94f61 100644 --- a/ui/src/app/edge/history/chpsoc/widget.component.html +++ b/ui/src/app/edge/history/chpsoc/widget.component.html @@ -1,4 +1,4 @@ - + {{ component.alias }} @@ -14,4 +14,4 @@ - \ No newline at end of file + diff --git a/ui/src/app/edge/history/chpsoc/widget.component.ts b/ui/src/app/edge/history/chpsoc/widget.component.ts index 45bdc7a995e..090b3ed1da8 100644 --- a/ui/src/app/edge/history/chpsoc/widget.component.ts +++ b/ui/src/app/edge/history/chpsoc/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { QueryHistoricTimeseriesDataResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesDataResponse'; diff --git a/ui/src/app/edge/history/common/autarchy/Autarchy.ts b/ui/src/app/edge/history/common/autarchy/Autarchy.ts index 03506c4004d..1407ff291e6 100644 --- a/ui/src/app/edge/history/common/autarchy/Autarchy.ts +++ b/ui/src/app/edge/history/common/autarchy/Autarchy.ts @@ -10,9 +10,6 @@ import { OverviewComponent } from './overview/overview'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ], declarations: [ FlatComponent, ChartComponent, diff --git a/ui/src/app/edge/history/common/autarchy/chart/chart.ts b/ui/src/app/edge/history/common/autarchy/chart/chart.ts index be1dc505d14..d6896069cee 100644 --- a/ui/src/app/edge/history/common/autarchy/chart/chart.ts +++ b/ui/src/app/edge/history/common/autarchy/chart/chart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; import { QueryHistoricTimeseriesEnergyResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesEnergyResponse'; diff --git a/ui/src/app/edge/history/common/autarchy/flat/flat.ts b/ui/src/app/edge/history/common/autarchy/flat/flat.ts index 8d167e1ef34..2cfb7757b8e 100644 --- a/ui/src/app/edge/history/common/autarchy/flat/flat.ts +++ b/ui/src/app/edge/history/common/autarchy/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, CurrentData, Utils } from '../../../../../shared/shared'; diff --git a/ui/src/app/edge/history/common/consumption/Consumption.ts b/ui/src/app/edge/history/common/consumption/Consumption.ts index f98ecbae967..bc0ccaadea4 100644 --- a/ui/src/app/edge/history/common/consumption/Consumption.ts +++ b/ui/src/app/edge/history/common/consumption/Consumption.ts @@ -11,9 +11,6 @@ import { OverviewComponent } from './overview/overview'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ], declarations: [ FlatComponent, ChartComponent, diff --git a/ui/src/app/edge/history/common/consumption/chart/chart.spec.ts b/ui/src/app/edge/history/common/consumption/chart/chart.spec.ts index db26031999e..9e124f57aed 100644 --- a/ui/src/app/edge/history/common/consumption/chart/chart.spec.ts +++ b/ui/src/app/edge/history/common/consumption/chart/chart.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { DummyConfig } from "src/app/shared/edge/edgeconfig.spec"; import { OeTester } from "src/app/shared/genericComponents/shared/testing/common"; @@ -31,9 +32,8 @@ describe('History Consumption', () => { DATA('Sonstiger: 63,3 kWh', [null, null, null, 0.5658045977011494, 0.3871815181518151, 0.561425925925926, 0.5732169811320755, 0.5658080808080809, 0.5879803921568627, 0.5842389380530973, 0.6074818181818182, 0.6050275229357799, 0.5956407766990292, 0.6025294117647059, 0.5781684210526317, 0.5816813186813187, 0.6495166666666666, 0.5602999999999999, 0.56703738317757, 0.6297672413793104, 0.5688613861386138, 0.5896039603960396, 0.5245090909090909, 0.5093300330033004, 0.6177196261682243, 0.608122641509434, 0.60278, 0.6508155339805825, 0.6058877551020408, 0.5943904761904762, 0.57636, 0.5650660377358491, 0.587495145631068, 0.626375, 0.6819428571428572, 0.6504629629629629, 0.6313738317757009, 0.68725, 0.5602452830188679, 0.5278952380952382, 0.6202222222222221, 0.4016355140186916, 0.5878130841121496, 0.604, 0.616295918367347, 0.5789405940594059, 0.5943396226415094, 0.6481078431372549, null, null, null, 0.5652183908045977, 0.5736481481481481, 0.5365462962962964, 0.558081081081081, 0.5747543859649124, 0.5743557692307693, 0.5969047619047619, 0.6540720720720721, 0.5018766666666667, 0.6128045977011494, 0.6361100000000001, 0.6137948717948718, 0.5388857142857142, 0.5371157894736842, 0.5511634615384615, 0.5588809523809524, 0.5591222222222223, 0.6185576923076923, 0.6543287671232877, 0.6160574712643678, 0.5889056603773585, 0.5787096774193549, 0.583036036036036, 0.572570093457944, 0.5952631578947368, 0.7450888888888888, 0.7073990610328639, -1.452950549450549, 2.233161450571287, 1.1458434343434352, 1.2180771929824568, 0.9178416666666669, 0.9020510752688171, 1.3458171717171723, 1.1460410714285718, 0.9853455165691996, 1.189936077481839, -1.4153224202237875, 0.6774427860696518, 0.9827305699481865, 0.8582012987012987, 0.7678924050632912, 0.761950495049505, 0.6821319796954315, 0.5954198473282443, 0.7021286549707602, 0.7309484536082475, 0.739, 0.730572864321608, 0.7547467532467532, 0.657373417721519, 0.6409480519480519, 0.6698156424581005, 0.7876280991735537, 2.931229357798165, 1.9542808988764044, 0.7775346534653466, 1.098538860103627, 1.2441524390243903, 2.9194913793103447, 2.9874188034188034, 3.4140294117647056, 1.2151999999999998, 2.7142824427480914, 2.6578703703703703, 2.8738923076923077, 4.013462078651685, 3.791560606060606, 2.845578947368421, 1.7741875, 0.89646, 1.2468691588785048, 1.0760386740331491, 0.8393491124260355, 1.194960199004975, 1.0562878787878787, 3.133, 3.78845625, 1.288096153846154, 3.4541666666666666, 2.0936967871485943, 2.384391025641026, 1.6707888888888887, 1.5589767441860465, 2.8620799999999997, 2.3241241379310345, 1.9640169491525423, 1.8084, 3.4660503597122303, 2.2974397590361444, 2.5300493827160495, 2.439358490566038, 2.0079060773480664, 1.7515, 1.4163181818181818, 1.4292298850574712, 1.4520298507462688, 1.4897204301075269, 1.6330952380952382, 1.8262928571428572, 1.6101904761904762, 1.680929292929293, 2.881743119266055, 3.5851634615384613, 3.6891666666666665, 3.6573402777777777, 3.6435348837209305, 3.7148645833333336, 3.731375, 3.74479, 3.6362363636363635, 4.273113924050633, 3.4461999999999997, 3.5387142857142857, 3.7906065573770493, 3.5276750000000003, 3.4676712328767123, 3.5595, 3.7982, 5.460666666666667, 1.3094406779661016, 1.5357454545454545, 3.4238260869565216, 3.3823636363636367, 3.4006315789473684, 2.95075, 3.386731707317073, 2.506, 1.4471666666666667, 1.4425999999999999, 0.946, 0.9425789473684211, 0.9507142857142856, 0.947, 0.9762857142857143, 1.7862857142857143, 1.5135777777777777, 1.4995625, 1.338, 1.3278125, 1.2739175257731958, 1.4387457627118645, 1.2484186046511627, 1.2866693548387098, 1.2848934911242604, 1.2237952755905512, 0.74809375, 0.8717684210526315, 0.8445338983050847, 0.7916749999999999, 0.8041932773109244, 0.7303737373737375, 0.7055024390243902, 0.6872407407407407, 0.6909939759036144, 0.751, 0.765139344262295, 0.686871794871795, 0.6697434210526315, 1.7678091603053436, 0.7246764705882353, 0.7482772277227723, 0.9401142857142858, 0.750368, 1.3660232558139536, 0.7274137931034482, 0.710719512195122, 0.6898555555555557, 0.739453488372093, 0.817875, 0.7304303797468354, 0.7355890410958904, 0.738225806451613, 1.906921739130435, 2.290785714285714, 1.2075072463768115, 1.1675890410958905, 1.2290208333333332, 1.1923777777777778, 1.2088717948717949, 1.367715909090909, 1.284223300970874, 1.1631739130434782, 1.15253, 1.1614545454545455, 1.2195681818181818, 1.183752808988764, 1.197778947368421, 1.2338888888888888, 1.275070588235294, 1.235554054054054, 1.20783908045977, 1.2416184210526318, 1.159042735042735, 1.1382948717948718, 1.1069915966386554, 1.1714504504504506, 1.223822429906542, 1.1221696428571428, 1.018892857142857, 0.9818285714285714, 0.9988363636363636, 0.8434776785714284, 1.4379482009925546, 1.4043499341238475, 1.7165029190992493, 1.781351488095238, 1.8585528255528247, 3.6707709585574175, 4.427144379844961, 4.301046195652174, 4.194778846153845, 4.150229357798166, 4.055816642120766, 3.976974967061925, 2.711485714285714, 1.0600519480519486, 0.954382608695652, 0.7914855072463762, 0.9272300420168067, 0.28519157088122604, 0.8123142857142857, 0.8095892857142857, 0.8664786324786324, 0.8778319327731092, 0.8108141592920354, 0.8157121212121212, 0.7706470588235294, 0.7633157894736842, 0.7815151515151515, 0.8075833333333333, 0.824743119266055, 0.8762151898734176, 0.882424, 0.7502213114754098, 0.675954954954955, 0.6371222222222223]), ], labels: LABELS(History.DAY.dataChannelWithValues.result.timestamps), - options: OeTester.ChartOptions.LINE_CHART_OPTIONS('hour', 'line', { - ["left"]: { scale: { min: -2, max: 11 }, ticks: { stepSize: 3.25 } }, - }), + options: OeTester.ChartOptions.LINE_CHART_OPTIONS('hour', 'line', {}, + ), }, }); } @@ -50,9 +50,7 @@ describe('History Consumption', () => { DATA('Sonstiger: 97,1 kWh', [0.6360308446701046, 0.9095864441102458, 0.7335808811402847, 0.5718131699718612, 0.9616446485934361, 0.6381720132750589, 1.02619037791046, 1.0145027809280902, 0.6390464755453447, 0.7096512022948386, 0.819995628581571, 0.935642813016216, 0.5254472085206601, 1.8851739940941776, 0.863444783197832, 1.1921009009810684, 0.5202376219567795, 0.7444045122530605, 0.901856453684574, 0.8104459244359407, 1.0830737950562592, 0.8714288985355628, 0.7780086316183336, 0.9590122781645534, 0.58627825521263, 0.8413148987905428, 0.8796668002158831, 0.6461976914860288, 0.8006733468796897, 0.8250297170962498, 0.825867009418173, 0.8407590098644301, 1.2782520162083928, 0.9299547986301717, 1.100431849409396, 0.881695115377627, -3.0801277220264014, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]), ], labels: LABELS(History.WEEK.dataChannelWithValues.result.timestamps), - options: OeTester.ChartOptions.LINE_CHART_OPTIONS('day', 'line', { - ["left"]: { scale: { min: -4, max: 5 }, ticks: { stepSize: 2.25 } }, - }), + options: OeTester.ChartOptions.LINE_CHART_OPTIONS('day', 'line', {}), }, }); } @@ -69,9 +67,7 @@ describe('History Consumption', () => { DATA('Sonstiger: 683,3 kWh', [1.1707523832922395, 0.941219247423001, 0.9813072961027226, 0.9147731344893865, 0.9179723719850451, 1.0221279641612842, 0.966452725206484, 0.9473972354717355, 0.9621558239123786, 1.0389565290773752, 0.719602808727641, 0.7164739453418082, 0.8312551527306125, 1.0513294309882544, 1.0642356695090618, 1.064549904209455, 0.8827148020023398, 1.0886724659066331, 0.9617007570391821, 1.2024244259088868, 1.4752269957477093, 1.149323439144603, 1.2039544333297325, 1.0275239776315197, 0.9588534351033258, 1.0445672257542986, 1.1028960998629487, 0.9010854319660326, 0.8581094263176695, null, null]), ], labels: LABELS(History.MONTH.energyPerPeriodChannelWithValues.result.timestamps), - options: OeTester.ChartOptions.BAR_CHART_OPTIONS('day', 'bar', { - ["left"]: { scale: { min: 0, max: 3 }, ticks: { stepSize: 0.75 } }, - }), + options: OeTester.ChartOptions.BAR_CHART_OPTIONS('day', 'bar', {}), }, }); } @@ -88,9 +84,7 @@ describe('History Consumption', () => { DATA('Sonstiger: 10.883,9 kWh', [1275.767, 1390.6460000000002, 1480.519, 1565.359, 1387.424, 1027.67, 1412.9189999999999, 1344.913, 0, 0, 0, 0]), ], labels: LABELS(History.YEAR.energyPerPeriodChannelWithValues.result.timestamps), - options: OeTester.ChartOptions.BAR_CHART_OPTIONS('month', 'bar', { - ["left"]: { scale: { min: 0, max: 2307 }, ticks: { stepSize: 576.75 } }, - }), + options: OeTester.ChartOptions.BAR_CHART_OPTIONS('month', 'bar', {}), }, }); } diff --git a/ui/src/app/edge/history/common/consumption/chart/chart.ts b/ui/src/app/edge/history/common/consumption/chart/chart.ts index e40ad5ff702..13e21ec841c 100644 --- a/ui/src/app/edge/history/common/consumption/chart/chart.ts +++ b/ui/src/app/edge/history/common/consumption/chart/chart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; diff --git a/ui/src/app/edge/history/common/consumption/flat/flat.ts b/ui/src/app/edge/history/common/consumption/flat/flat.ts index ddfde940df7..947b0c7c2c8 100644 --- a/ui/src/app/edge/history/common/consumption/flat/flat.ts +++ b/ui/src/app/edge/history/common/consumption/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, CurrentData, EdgeConfig } from '../../../../../shared/shared'; diff --git a/ui/src/app/edge/history/common/energy/chart/channels.spec.ts b/ui/src/app/edge/history/common/energy/chart/channels.spec.ts index 9d76eb3cc97..632d08a1291 100644 --- a/ui/src/app/edge/history/common/energy/chart/channels.spec.ts +++ b/ui/src/app/edge/history/common/energy/chart/channels.spec.ts @@ -14,13 +14,13 @@ export namespace History { "responsive": true, "maintainAspectRatio": false, "elements": { "point": { "radius": 0, "hitRadius": 0, "hoverRadius": 0 }, "line": { "stepped": false, "fill": true } }, "datasets": { "bar": {}, "line": {} }, "plugins": { "colors": { "enabled": false }, "legend": { "display": true, "position": "bottom", "labels": { "color": '' } }, "tooltip": { "intersect": false, "mode": "index", "callbacks": {} } }, "scales": { "x": { "stacked": true, "offset": false, "type": "time", "ticks": { "source": "auto", "maxTicksLimit": 31 }, "bounds": "ticks", "adapters": { "date": { "locale": { "code": "de", "formatLong": {}, "localize": {}, "match": {}, "options": { "weekStartsOn": 1, "firstWeekContainsDate": 4 } } } }, "time": { "unit": period as TimeUnit, "displayFormats": { "datetime": "yyyy-MM-dd HH:mm:ss", "millisecond": "SSS [ms]", "second": "HH:mm:ss a", "minute": "HH:mm", "hour": "HH:00", "day": "dd", "week": "ll", "month": "MM", "quarter": "[Q]Q - YYYY", "year": "yyyy" } } }, "left": { - ...options["left"].scale, ...(chartType === 'line' ? { stacked: false } : {}), "title": { "text": "kW", "display": true, "padding": 5, "font": { "size": 11 } }, "position": "left", "grid": { "display": true }, - "ticks": { ...options["left"].ticks, "color": '', "padding": 5, "maxTicksLimit": ChartConstants.NUMBER_OF_Y_AXIS_TICKS }, + ...options["left"]?.scale, ...(chartType === 'line' ? { stacked: false } : {}), "title": { "text": "kW", "display": true, "padding": 5, "font": { "size": 11 } }, "position": "left", "grid": { "display": true }, + "ticks": { ...options["left"]?.ticks, "color": '', "padding": 5, "maxTicksLimit": ChartConstants.NUMBER_OF_Y_AXIS_TICKS }, }, "right": { - ...options["right"].scale, ...(chartType === 'line' ? { stacked: false } : {}), "beginAtZero": true, "max": 100, "min": 0, "type": "linear", "title": { "text": "%", "display": true, "font": { "size": 11 }, "padding": 5 }, "position": "right", "grid": { "display": false }, + ...options["right"]?.scale, ...(chartType === 'line' ? { stacked: false } : {}), "beginAtZero": true, "max": 100, "min": 0, "type": "linear", "title": { "text": "%", "display": true, "font": { "size": 11 }, "padding": 5 }, "position": "right", "grid": { "display": false }, "ticks": { - ...options["right"].ticks, + ...options["right"]?.ticks, "color": '', "padding": 5, "maxTicksLimit": ChartConstants.NUMBER_OF_Y_AXIS_TICKS, @@ -35,9 +35,9 @@ export namespace History { "responsive": true, "maintainAspectRatio": false, "elements": { "point": { "radius": 0, "hitRadius": 0, "hoverRadius": 0 }, "line": { "stepped": false, "fill": true } }, "datasets": { "bar": { "barPercentage": 1 }, "line": {} }, "plugins": { "colors": { "enabled": false }, "legend": { "display": true, "position": "bottom", "labels": { "color": '' } }, "tooltip": { "intersect": false, "mode": "x", "callbacks": {} } }, "scales": { "x": { "stacked": true, "offset": true, "type": "time", "ticks": { "source": "auto", "maxTicksLimit": 31 }, "bounds": "ticks", "adapters": { "date": { "locale": { "code": "de", "formatLong": {}, "localize": {}, "match": {}, "options": { "weekStartsOn": 1, "firstWeekContainsDate": 4 } } } }, "time": { "unit": period as TimeUnit, "displayFormats": { "datetime": "yyyy-MM-dd HH:mm:ss", "millisecond": "SSS [ms]", "second": "HH:mm:ss a", "minute": "HH:mm", "hour": "HH:00", "day": "dd", "week": "ll", "month": "MM", "quarter": "[Q]Q - YYYY", "year": "yyyy" } } }, "left": { - ...options["left"].scale, ...(chartType === 'line' ? { stacked: false } : {}), "title": { "text": "kWh", "display": true, "padding": 5, "font": { "size": 11 } }, "position": "left", "grid": { "display": true }, + ...options["left"]?.scale, ...(chartType === 'line' ? { stacked: false } : {}), "title": { "text": "kWh", "display": true, "padding": 5, "font": { "size": 11 } }, "position": "left", "grid": { "display": true }, "ticks": { - ...options["left"].ticks, + ...options["left"]?.ticks, "color": '', "padding": 5, "maxTicksLimit": ChartConstants.NUMBER_OF_Y_AXIS_TICKS, diff --git a/ui/src/app/edge/history/common/energy/chart/chart.spec.ts b/ui/src/app/edge/history/common/energy/chart/chart.spec.ts index f2fde360c3e..6520137afc5 100644 --- a/ui/src/app/edge/history/common/energy/chart/chart.spec.ts +++ b/ui/src/app/edge/history/common/energy/chart/chart.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { History } from "src/app/edge/history/common/energy/chart/channels.spec"; import { DummyConfig } from "src/app/shared/edge/edgeconfig.spec"; @@ -33,7 +34,7 @@ describe('History EnergyMonitor', () => { ], labels: LABELS(History.DAY.dataChannelWithValues.result.timestamps), options: History.LINE_CHART_OPTIONS('hour', 'line', { - ["left"]: { scale: { min: 0, max: 7 }, ticks: { stepSize: 1.75 } }, ["right"]: { scale: { min: 0, max: 1003 }, ticks: { stepSize: 25 } }, + ['right']: { ticks: { stepSize: 20 }, scale: null }, }), }, }); @@ -54,9 +55,7 @@ describe('History EnergyMonitor', () => { DATA('Ladezustand', History.WEEK.dataChannelWithValues.result.data['_sum/EssSoc']), ], labels: LABELS(History.WEEK.dataChannelWithValues.result.timestamps), - options: History.LINE_CHART_OPTIONS('day', 'line', { - ["left"]: { scale: { min: 0, max: 9 }, ticks: { stepSize: 2.25 } }, ["right"]: { scale: { min: 0, max: 1003 }, ticks: { stepSize: 25 } }, - }), + options: History.LINE_CHART_OPTIONS('day', 'line', { ['right']: { ticks: { stepSize: 20 }, scale: null } }), }, }); } @@ -79,9 +78,7 @@ describe('History EnergyMonitor', () => { DATA('Verbrauch: 9.976,1 kWh', [320.342, 346.615, 341.433, 333.054, 358.458, 347.872, 289.283, null, 556.51, 311.366, 314.722, 355.556, 381.671, 384.558, 366.19, 349.336, 303.696, 288.727, 357.434, 388.659, 402.625, null, 713.771, 320.238, 332.099, null, 756.429, 384.136, 371.322, null]), ], labels: LABELS(History.MONTH.energyPerPeriodChannelWithValues.result.timestamps), - options: History.BAR_CHART_OPTIONS('day', 'bar', { - ["left"]: { scale: { min: 0, max: 1579 }, ticks: { stepSize: 394.75 } }, - }), + options: History.BAR_CHART_OPTIONS('day', 'bar', {}), }, }); } @@ -104,9 +101,7 @@ describe('History EnergyMonitor', () => { DATA('Verbrauch: 58.573,4 kWh', [11634.885, 8207.927, 8976.354, 8311.835, 10341.804, 9976.102, 975.807, null, null, null, null, null]), ], labels: LABELS(History.YEAR.energyPerPeriodChannelWithValues.result.timestamps), - options: History.BAR_CHART_OPTIONS('month', 'bar', { - ["left"]: { scale: { min: 0, max: 22491 }, ticks: { stepSize: 5622.75 } }, - }), + options: History.BAR_CHART_OPTIONS('month', 'bar', {}), }, }); } @@ -119,7 +114,6 @@ describe('History EnergyMonitor', () => { data: [], labels: LABELS(History.YEAR.energyPerPeriodChannelWithValues.result.timestamps), options: History.BAR_CHART_OPTIONS('month', 'bar', { - ["left"]: { scale: {}, ticks: {} }, }), }, }); @@ -143,9 +137,7 @@ describe('History EnergyMonitor', () => { DATA('Verbrauch: 58.573,4 kWh', [11634.885, 8207.927, 8976.354, 8311.835, 10341.804, 9976.102, 975.807, null, null, null, null, null]), ], labels: LABELS(History.YEAR.energyPerPeriodChannelWithValues.result.timestamps), - options: History.BAR_CHART_OPTIONS('month', 'bar', { - ["left"]: { scale: { min: 0, max: 12839 }, ticks: { stepSize: 3209.75 } }, ["right"]: { scale: { min: 0, max: 1003 }, ticks: { stepSize: 25 } }, - }), + options: History.BAR_CHART_OPTIONS('month', 'bar', {}), }, }); } @@ -165,9 +157,7 @@ describe('History EnergyMonitor', () => { DATA('Verbrauch: 58.573,4 kWh', [11634.885, 8207.927, 8976.354, 8311.835, 10341.804, 9976.102, 975.807, null, null, null, null, null]), ], labels: LABELS(History.YEAR.energyPerPeriodChannelWithValues.result.timestamps), - options: History.BAR_CHART_OPTIONS('month', 'bar', { - ["left"]: { scale: { min: 0, max: 12839 }, ticks: { stepSize: 3209.75 } }, - }), + options: History.BAR_CHART_OPTIONS('month', 'bar', {}), }, }); } @@ -187,9 +177,7 @@ describe('History EnergyMonitor', () => { DATA('Verbrauch: 58.573,4 kWh', [11634.885, 8207.927, 8976.354, 8311.835, 10341.804, 9976.102, 975.807, null, null, null, null, null]), ], labels: LABELS(History.YEAR.energyPerPeriodChannelWithValues.result.timestamps), - options: History.BAR_CHART_OPTIONS('month', 'bar', { - ["left"]: { scale: { min: 0, max: 11635 }, ticks: { stepSize: 2908.75 } }, - }), + options: History.BAR_CHART_OPTIONS('month', 'bar', {}), }, }); } diff --git a/ui/src/app/edge/history/common/energy/chart/chart.ts b/ui/src/app/edge/history/common/energy/chart/chart.ts index 2e3a22cc496..8e65cb0a53f 100644 --- a/ui/src/app/edge/history/common/energy/chart/chart.ts +++ b/ui/src/app/edge/history/common/energy/chart/chart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; diff --git a/ui/src/app/edge/history/common/energy/energy.ts b/ui/src/app/edge/history/common/energy/energy.ts index 6b7704256e2..fe07c430784 100644 --- a/ui/src/app/edge/history/common/energy/energy.ts +++ b/ui/src/app/edge/history/common/energy/energy.ts @@ -10,10 +10,6 @@ import { FlatComponent } from './flat/flat'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ChartComponent, - ], declarations: [ FlatComponent, ChartComponent, diff --git a/ui/src/app/edge/history/common/energy/flat/flat.ts b/ui/src/app/edge/history/common/energy/flat/flat.ts index 2ff36ee1489..f69ce558271 100644 --- a/ui/src/app/edge/history/common/energy/flat/flat.ts +++ b/ui/src/app/edge/history/common/energy/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, CurrentData, Utils } from '../../../../../shared/shared'; diff --git a/ui/src/app/edge/history/common/grid/chart/chart.spec.ts b/ui/src/app/edge/history/common/grid/chart/chart.spec.ts index e44f5fc6a5c..d0af9ae56dd 100644 --- a/ui/src/app/edge/history/common/grid/chart/chart.spec.ts +++ b/ui/src/app/edge/history/common/grid/chart/chart.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { History } from "src/app/edge/history/common/energy/chart/channels.spec"; import { DummyConfig } from "src/app/shared/edge/edgeconfig.spec"; import { OeTester } from "src/app/shared/genericComponents/shared/testing/common"; @@ -27,10 +28,7 @@ describe('History Grid', () => { DATA('Bezug: 0,9 kWh', [null, null, null, 0.031, 0.018, 0, 0.02, 0.016, 0.015, 0.014, 0.009, 0.02, 0.025, 0.025, 0.025, 0.021, 0.012, 0.009, 0.01, 0.011, 0.005, 0.003, 0, 0.015, 0.018, 0.023, 0, 0, 0, 0.002, 0.002, 0.003, 0.015, 0.008, 0.022, 0.027, 0.016, 0.003, 0.002, 0, 0.028, 0.027, 0.017, 0.001, 0, 0, 0, null, null, null, null, 0.011, 0.01, 0.004, 0.006, 0.007, 0.018, 0.008, 0.012, 0.009, 0.004, 0.013, 0.015, 0.012, 0, 0, 0, 0.002, 0, 0.005, 0.001, 0.03, 0.062, 0, 0, 0, 0, 0, 0, 0, 0, 0.015, 0.005, 0.004, 0.007, 0, 0, 0, 0, 0, 0, 0, 0.005, 0, 0, 0, 0, 0, 0, 0.021, 0, 0, 0, 0, 0, 0.003, 0, 0.004, 0, 0, 0.032, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]), ], labels: LABELS(History.DAY.dataChannelWithValues.result.timestamps), - options: OeTester.ChartOptions.LINE_CHART_OPTIONS('hour', 'line', { - ["left"]: { scale: { min: 0, max: 7 }, ticks: { stepSize: 1.75 } }, - }, - ), + options: OeTester.ChartOptions.LINE_CHART_OPTIONS('hour', 'line', {}), }, }, false); } @@ -44,11 +42,8 @@ describe('History Grid', () => { DATA('Bezug: 2,4 kWh', [0, 0.011916666666666666, 0.01633333333333333, 0.00609090909090909, 0.015333333333333334, 0.011666666666666665, 0.0024166666666666664, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.02425, 0.004416666666666667, 0.0035833333333333333, 0, 0, 0, 0.04441666666666667, 0, 0.013111111111111112, 0.001, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0011666666666666668, 0, 0, 0, 0.0015833333333333333, 0.013333333333333334, 0.020416666666666666, 0.01125, 0.019727272727272725, 0.012444444444444445, 0.009583333333333334, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.007666666666666667, 0, 0.0023333333333333335, 0.0125, 0.01609090909090909, 0.02016666666666667, 0.014083333333333333, 0.006363636363636363, 0.01955555555555556, 0.04841666666666666, 0.011166666666666667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.014222222222222221, 0.00225, 0, 0.0036666666666666666, 0.032916666666666664, 0.014666666666666666, 0.0135, 0.017363636363636362, 0.013333333333333334, 0.022083333333333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0009166666666666666, 0, 0.0021666666666666666, 0, 0, 0, 0.0005, 0.04841666666666666, 0, 0.005555555555555556, 0.02716666666666667, 0.017333333333333333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0023333333333333335, 0.008333333333333333, 0.003, 0.015916666666666666, 0.00325, 0, 0.004333333333333333, 0.001, 0, 0, 0.019545454545454546, 0.0017777777777777776, 0.006416666666666667, 0.017666666666666667, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.0058, 0.005625, 0, 0]), ], labels: LABELS(History.WEEK.dataChannelWithValues.result.timestamps), - options: OeTester.ChartOptions.LINE_CHART_OPTIONS('day', 'line', { - ["left"]: { scale: { min: 0, max: 7 }, ticks: { stepSize: 1.75 } }, - }), + options: OeTester.ChartOptions.LINE_CHART_OPTIONS('day', 'line', {}), }, - }, false); } { @@ -61,9 +56,7 @@ describe('History Grid', () => { DATA('Bezug: 773 kWh', [16, 6, 3, 3, 5, 48, 4, null, 5, 26, 17, 62, 8, 66, 13, 21, 4, 3, 18, 27, 29, null, 118, 85, 2, null, 72, 28, 84, null]), ], labels: LABELS(History.MONTH.energyPerPeriodChannelWithValues.result.timestamps), - options: OeTester.ChartOptions.BAR_CHART_OPTIONS('day', 'bar', { - ["left"]: { scale: { min: 0, max: 1003 }, ticks: { stepSize: 250.75 } }, - }), + options: OeTester.ChartOptions.BAR_CHART_OPTIONS('day', 'bar', {}), }, }, false); @@ -78,9 +71,7 @@ describe('History Grid', () => { DATA('Bezug: 23.209 kWh', [9829, 4812, 2915, 2036, 2712, 773, 94, null, null, null, null, null]), ], labels: LABELS(History.YEAR.energyPerPeriodChannelWithValues.result.timestamps), - options: OeTester.ChartOptions.BAR_CHART_OPTIONS('month', 'bar', { - ["left"]: { scale: { min: 0, max: 12839 }, ticks: { stepSize: 3209.75 } }, - }), + options: OeTester.ChartOptions.BAR_CHART_OPTIONS('month', 'bar', {}), }, }, false); } diff --git a/ui/src/app/edge/history/common/grid/chart/chart.ts b/ui/src/app/edge/history/common/grid/chart/chart.ts index fd5507e76a7..c8e5fc00b15 100644 --- a/ui/src/app/edge/history/common/grid/chart/chart.ts +++ b/ui/src/app/edge/history/common/grid/chart/chart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; diff --git a/ui/src/app/edge/history/common/grid/grid.ts b/ui/src/app/edge/history/common/grid/grid.ts index 8971a15d9f0..85814e76c9f 100644 --- a/ui/src/app/edge/history/common/grid/grid.ts +++ b/ui/src/app/edge/history/common/grid/grid.ts @@ -11,9 +11,6 @@ import { OverviewComponent } from './overview/overview'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ], declarations: [ FlatComponent, ChartComponent, diff --git a/ui/src/app/edge/history/common/production/chart/chargerChart.ts b/ui/src/app/edge/history/common/production/chart/chargerChart.ts index 83f1b903235..593e4d4bc19 100644 --- a/ui/src/app/edge/history/common/production/chart/chargerChart.ts +++ b/ui/src/app/edge/history/common/production/chart/chargerChart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; import { QueryHistoricTimeseriesEnergyResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesEnergyResponse'; diff --git a/ui/src/app/edge/history/common/production/chart/productionMeterChart.ts b/ui/src/app/edge/history/common/production/chart/productionMeterChart.ts index 798f886c6c4..baf66d6a040 100644 --- a/ui/src/app/edge/history/common/production/chart/productionMeterChart.ts +++ b/ui/src/app/edge/history/common/production/chart/productionMeterChart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; import { QueryHistoricTimeseriesEnergyResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesEnergyResponse'; diff --git a/ui/src/app/edge/history/common/production/chart/totalAcChart.ts b/ui/src/app/edge/history/common/production/chart/totalAcChart.ts index af06fd9527c..c788ebf8964 100644 --- a/ui/src/app/edge/history/common/production/chart/totalAcChart.ts +++ b/ui/src/app/edge/history/common/production/chart/totalAcChart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; import { QueryHistoricTimeseriesEnergyResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesEnergyResponse'; diff --git a/ui/src/app/edge/history/common/production/chart/totalChart.ts b/ui/src/app/edge/history/common/production/chart/totalChart.ts index 2cb1514eb6a..0f2deef3cc5 100644 --- a/ui/src/app/edge/history/common/production/chart/totalChart.ts +++ b/ui/src/app/edge/history/common/production/chart/totalChart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; import { QueryHistoricTimeseriesEnergyResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesEnergyResponse'; diff --git a/ui/src/app/edge/history/common/production/chart/totalDcChart.ts b/ui/src/app/edge/history/common/production/chart/totalDcChart.ts index 7e6a6764159..a9b87962b9a 100644 --- a/ui/src/app/edge/history/common/production/chart/totalDcChart.ts +++ b/ui/src/app/edge/history/common/production/chart/totalDcChart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; import { QueryHistoricTimeseriesEnergyResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesEnergyResponse'; diff --git a/ui/src/app/edge/history/common/production/overview/overview.html b/ui/src/app/edge/history/common/production/overview/overview.html index 36b566df443..c4706da4aaf 100644 --- a/ui/src/app/edge/history/common/production/overview/overview.html +++ b/ui/src/app/edge/history/common/production/overview/overview.html @@ -33,4 +33,4 @@ - \ No newline at end of file + diff --git a/ui/src/app/edge/history/common/production/production.ts b/ui/src/app/edge/history/common/production/production.ts index 038dc44c818..890070a3e15 100644 --- a/ui/src/app/edge/history/common/production/production.ts +++ b/ui/src/app/edge/history/common/production/production.ts @@ -15,15 +15,6 @@ import { OverviewComponent } from './overview/overview'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - OverviewComponent, - ProductionMeterChartComponent, - TotalDcChartComponent, - TotalAcChartComponent, - TotalChartComponent, - ChargerChartComponent, - ], declarations: [ FlatComponent, OverviewComponent, diff --git a/ui/src/app/edge/history/common/selfconsumption/SelfConsumption.ts b/ui/src/app/edge/history/common/selfconsumption/SelfConsumption.ts index 49ece21cfab..e67a47ad931 100644 --- a/ui/src/app/edge/history/common/selfconsumption/SelfConsumption.ts +++ b/ui/src/app/edge/history/common/selfconsumption/SelfConsumption.ts @@ -10,9 +10,6 @@ import { OverviewComponent } from "./overview/overview"; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ], declarations: [ FlatComponent, ChartComponent, diff --git a/ui/src/app/edge/history/common/selfconsumption/chart/chart.component.ts b/ui/src/app/edge/history/common/selfconsumption/chart/chart.component.ts index af0c382d9ed..0974e5f5bfe 100644 --- a/ui/src/app/edge/history/common/selfconsumption/chart/chart.component.ts +++ b/ui/src/app/edge/history/common/selfconsumption/chart/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; import { QueryHistoricTimeseriesEnergyResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesEnergyResponse'; diff --git a/ui/src/app/edge/history/common/selfconsumption/flat/flat.ts b/ui/src/app/edge/history/common/selfconsumption/flat/flat.ts index 91598222c82..4bcd1ba6b0b 100644 --- a/ui/src/app/edge/history/common/selfconsumption/flat/flat.ts +++ b/ui/src/app/edge/history/common/selfconsumption/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { CurrentData, Utils, ChannelAddress } from 'src/app/shared/shared'; diff --git a/ui/src/app/edge/history/delayedselltogrid/chart.component.ts b/ui/src/app/edge/history/delayedselltogrid/chart.component.ts index e6f40fe98a9..e51dd2b147c 100644 --- a/ui/src/app/edge/history/delayedselltogrid/chart.component.ts +++ b/ui/src/app/edge/history/delayedselltogrid/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/delayedselltogrid/symmetricpeakshavingchartoverview/delayedselltogridchartoverview.component.ts b/ui/src/app/edge/history/delayedselltogrid/symmetricpeakshavingchartoverview/delayedselltogridchartoverview.component.ts index 715208abce1..fa597ea5577 100644 --- a/ui/src/app/edge/history/delayedselltogrid/symmetricpeakshavingchartoverview/delayedselltogridchartoverview.component.ts +++ b/ui/src/app/edge/history/delayedselltogrid/symmetricpeakshavingchartoverview/delayedselltogridchartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service } from '../../../../shared/shared'; diff --git a/ui/src/app/edge/history/delayedselltogrid/widget.component.ts b/ui/src/app/edge/history/delayedselltogrid/widget.component.ts index 7e56e114434..3b4e3442ca5 100644 --- a/ui/src/app/edge/history/delayedselltogrid/widget.component.ts +++ b/ui/src/app/edge/history/delayedselltogrid/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ActivatedRoute } from '@angular/router'; import { Component, Input, OnInit } from '@angular/core'; import { DefaultTypes } from 'src/app/shared/service/defaulttypes'; diff --git a/ui/src/app/edge/history/fixdigitaloutput/fixdigitaloutputchartoverview/fixdigitaloutputchartoverview.component.ts b/ui/src/app/edge/history/fixdigitaloutput/fixdigitaloutputchartoverview/fixdigitaloutputchartoverview.component.ts index 623410cef2e..d62eb1766cd 100644 --- a/ui/src/app/edge/history/fixdigitaloutput/fixdigitaloutputchartoverview/fixdigitaloutputchartoverview.component.ts +++ b/ui/src/app/edge/history/fixdigitaloutput/fixdigitaloutputchartoverview/fixdigitaloutputchartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service, Utils } from '../../../../shared/shared'; diff --git a/ui/src/app/edge/history/fixdigitaloutput/singlechart.component.ts b/ui/src/app/edge/history/fixdigitaloutput/singlechart.component.ts index 136004e5499..7dba882fa89 100644 --- a/ui/src/app/edge/history/fixdigitaloutput/singlechart.component.ts +++ b/ui/src/app/edge/history/fixdigitaloutput/singlechart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/fixdigitaloutput/totalchart.component.ts b/ui/src/app/edge/history/fixdigitaloutput/totalchart.component.ts index 3f678bf5ccc..ede98a6e38b 100644 --- a/ui/src/app/edge/history/fixdigitaloutput/totalchart.component.ts +++ b/ui/src/app/edge/history/fixdigitaloutput/totalchart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/fixdigitaloutput/widget.component.ts b/ui/src/app/edge/history/fixdigitaloutput/widget.component.ts index 8716004c0fb..f4990c751fb 100644 --- a/ui/src/app/edge/history/fixdigitaloutput/widget.component.ts +++ b/ui/src/app/edge/history/fixdigitaloutput/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { QueryHistoricTimeseriesDataResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesDataResponse'; diff --git a/ui/src/app/edge/history/grid/chart.component.ts b/ui/src/app/edge/history/grid/chart.component.ts index 43cc204ee76..285c8d4961e 100644 --- a/ui/src/app/edge/history/grid/chart.component.ts +++ b/ui/src/app/edge/history/grid/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { formatNumber } from '@angular/common'; import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; diff --git a/ui/src/app/edge/history/gridoptimizedcharge/chart.component.ts b/ui/src/app/edge/history/gridoptimizedcharge/chart.component.ts index 06743192d12..828617a2266 100644 --- a/ui/src/app/edge/history/gridoptimizedcharge/chart.component.ts +++ b/ui/src/app/edge/history/gridoptimizedcharge/chart.component.ts @@ -1,7 +1,7 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { ChartConstants } from 'src/app/shared/genericComponents/chart/chart.constants'; import { DefaultTypes } from 'src/app/shared/service/defaulttypes'; import { ChartAxis, HistoryUtils, YAxisTitle } from 'src/app/shared/service/utils'; @@ -159,9 +159,10 @@ export class GridOptimizedChargeChartComponent extends AbstractHistoryChart impl label: this.translate.instant('General.soc'), data: socData, hidden: false, - yAxisID: 'yAxis2', + yAxisID: ChartAxis.RIGHT, position: 'right', borderDash: [10, 10], + unit: YAxisTitle.PERCENTAGE, }); this.colors.push({ backgroundColor: 'rgba(189, 195, 199,0.05)', @@ -190,20 +191,14 @@ export class GridOptimizedChargeChartComponent extends AbstractHistoryChart impl } private applyControllerSpecificOptions() { - const yAxisRight: HistoryUtils.yAxes = { - unit: YAxisTitle.PERCENTAGE, - position: 'right', - yAxisId: ChartAxis.RIGHT, - displayGrid: false, - }; + const yAxisRight: HistoryUtils.yAxes = { unit: YAxisTitle.PERCENTAGE, position: 'right', yAxisId: ChartAxis.RIGHT, displayGrid: false }; + const yAxisLeft: HistoryUtils.yAxes = { position: 'left', unit: YAxisTitle.ENERGY, yAxisId: ChartAxis.LEFT }; const locale = this.service.translate.currentLang; const showYAxisTitle = true; - const yAxisLeft: HistoryUtils.yAxes = { position: 'left', unit: YAxisTitle.ENERGY, yAxisId: ChartAxis.LEFT }; [yAxisRight, yAxisLeft].forEach(yAxis => { - const scaleOptions = ChartConstants.getScaleOptions(this.datasets, yAxis); - this.options = NewAbstractHistoryChart.getYAxisOptions(this.options, yAxis, this.translate, 'line', locale, showYAxisTitle, scaleOptions); + this.options = NewAbstractHistoryChart.getYAxisOptions(this.options, yAxis, this.translate, 'line', locale, showYAxisTitle); }); this.datasets = this.datasets.map((el, index, arr) => { diff --git a/ui/src/app/edge/history/gridoptimizedcharge/gridoptimizedchargechartoverview/gridoptimizedchargechartoverview.component.ts b/ui/src/app/edge/history/gridoptimizedcharge/gridoptimizedchargechartoverview/gridoptimizedchargechartoverview.component.ts index 081dbb9895c..0f2dc33a6aa 100644 --- a/ui/src/app/edge/history/gridoptimizedcharge/gridoptimizedchargechartoverview/gridoptimizedchargechartoverview.component.ts +++ b/ui/src/app/edge/history/gridoptimizedcharge/gridoptimizedchargechartoverview/gridoptimizedchargechartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service } from '../../../../shared/shared'; diff --git a/ui/src/app/edge/history/gridoptimizedcharge/sellToGridLimitChart.component.ts b/ui/src/app/edge/history/gridoptimizedcharge/sellToGridLimitChart.component.ts index fca488700f2..0c556547ae4 100644 --- a/ui/src/app/edge/history/gridoptimizedcharge/sellToGridLimitChart.component.ts +++ b/ui/src/app/edge/history/gridoptimizedcharge/sellToGridLimitChart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/gridoptimizedcharge/widget.component.ts b/ui/src/app/edge/history/gridoptimizedcharge/widget.component.ts index 122ef28ec7a..fd5361b5b33 100644 --- a/ui/src/app/edge/history/gridoptimizedcharge/widget.component.ts +++ b/ui/src/app/edge/history/gridoptimizedcharge/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { DefaultTypes } from 'src/app/shared/service/defaulttypes'; diff --git a/ui/src/app/edge/history/heatingelement/chart.component.ts b/ui/src/app/edge/history/heatingelement/chart.component.ts index 3d933402698..d2647126584 100644 --- a/ui/src/app/edge/history/heatingelement/chart.component.ts +++ b/ui/src/app/edge/history/heatingelement/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/heatingelement/heatingelementchartoverview/heatingelementchartoverview.component.ts b/ui/src/app/edge/history/heatingelement/heatingelementchartoverview/heatingelementchartoverview.component.ts index 8feb42315f4..795f4503b0f 100644 --- a/ui/src/app/edge/history/heatingelement/heatingelementchartoverview/heatingelementchartoverview.component.ts +++ b/ui/src/app/edge/history/heatingelement/heatingelementchartoverview/heatingelementchartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service } from '../../../../shared/shared'; diff --git a/ui/src/app/edge/history/heatingelement/widget.component.ts b/ui/src/app/edge/history/heatingelement/widget.component.ts index 0f8c2a62e16..54c59de124b 100644 --- a/ui/src/app/edge/history/heatingelement/widget.component.ts +++ b/ui/src/app/edge/history/heatingelement/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { QueryHistoricTimeseriesDataResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesDataResponse'; diff --git a/ui/src/app/edge/history/heatpump/chart.component.ts b/ui/src/app/edge/history/heatpump/chart.component.ts index 5350e669af1..a026c2e3d71 100644 --- a/ui/src/app/edge/history/heatpump/chart.component.ts +++ b/ui/src/app/edge/history/heatpump/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/heatpump/heatpumpchartoverview/heatpumpchartoverview.component.ts b/ui/src/app/edge/history/heatpump/heatpumpchartoverview/heatpumpchartoverview.component.ts index a4c248c80e1..93e94726d5e 100644 --- a/ui/src/app/edge/history/heatpump/heatpumpchartoverview/heatpumpchartoverview.component.ts +++ b/ui/src/app/edge/history/heatpump/heatpumpchartoverview/heatpumpchartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/history/heatpump/widget.component.ts b/ui/src/app/edge/history/heatpump/widget.component.ts index e8e42316c5b..14ef01243f7 100644 --- a/ui/src/app/edge/history/heatpump/widget.component.ts +++ b/ui/src/app/edge/history/heatpump/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/history/history.component.ts b/ui/src/app/edge/history/history.component.ts index 0441bc8823d..61166b75f5d 100644 --- a/ui/src/app/edge/history/history.component.ts +++ b/ui/src/app/edge/history/history.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/historydataservice.ts b/ui/src/app/edge/history/historydataservice.ts index 60efc278739..51e5a033ab5 100644 --- a/ui/src/app/edge/history/historydataservice.ts +++ b/ui/src/app/edge/history/historydataservice.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Inject, Injectable } from "@angular/core"; import { DataService } from "../../shared/genericComponents/shared/dataservice"; diff --git a/ui/src/app/edge/history/peakshaving/asymmetric/asymmetricpeakshavingchartoverview/asymmetricpeakshavingchartoverview.component.ts b/ui/src/app/edge/history/peakshaving/asymmetric/asymmetricpeakshavingchartoverview/asymmetricpeakshavingchartoverview.component.ts index 7d5cca39ae8..9e012f13202 100644 --- a/ui/src/app/edge/history/peakshaving/asymmetric/asymmetricpeakshavingchartoverview/asymmetricpeakshavingchartoverview.component.ts +++ b/ui/src/app/edge/history/peakshaving/asymmetric/asymmetricpeakshavingchartoverview/asymmetricpeakshavingchartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service } from '../../../../../shared/shared'; diff --git a/ui/src/app/edge/history/peakshaving/asymmetric/chart.component.ts b/ui/src/app/edge/history/peakshaving/asymmetric/chart.component.ts index 14c7fc670fa..158d68ca676 100644 --- a/ui/src/app/edge/history/peakshaving/asymmetric/chart.component.ts +++ b/ui/src/app/edge/history/peakshaving/asymmetric/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/peakshaving/asymmetric/widget.component.ts b/ui/src/app/edge/history/peakshaving/asymmetric/widget.component.ts index dfa54214f11..daf215d6681 100644 --- a/ui/src/app/edge/history/peakshaving/asymmetric/widget.component.ts +++ b/ui/src/app/edge/history/peakshaving/asymmetric/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ActivatedRoute } from '@angular/router'; import { Component, Input, OnInit } from '@angular/core'; import { DefaultTypes } from 'src/app/shared/service/defaulttypes'; diff --git a/ui/src/app/edge/history/peakshaving/symmetric/chart.component.ts b/ui/src/app/edge/history/peakshaving/symmetric/chart.component.ts index 11d986d8b25..8cd625e16b9 100644 --- a/ui/src/app/edge/history/peakshaving/symmetric/chart.component.ts +++ b/ui/src/app/edge/history/peakshaving/symmetric/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/peakshaving/symmetric/symmetricpeakshavingchartoverview/symmetricpeakshavingchartoverview.component.ts b/ui/src/app/edge/history/peakshaving/symmetric/symmetricpeakshavingchartoverview/symmetricpeakshavingchartoverview.component.ts index 452bd2b603b..9da6f44c1bc 100644 --- a/ui/src/app/edge/history/peakshaving/symmetric/symmetricpeakshavingchartoverview/symmetricpeakshavingchartoverview.component.ts +++ b/ui/src/app/edge/history/peakshaving/symmetric/symmetricpeakshavingchartoverview/symmetricpeakshavingchartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service } from '../../../../../shared/shared'; diff --git a/ui/src/app/edge/history/peakshaving/symmetric/widget.component.ts b/ui/src/app/edge/history/peakshaving/symmetric/widget.component.ts index 923c2e27646..179d46e5e31 100644 --- a/ui/src/app/edge/history/peakshaving/symmetric/widget.component.ts +++ b/ui/src/app/edge/history/peakshaving/symmetric/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { DefaultTypes } from 'src/app/shared/service/defaulttypes'; diff --git a/ui/src/app/edge/history/peakshaving/timeslot/chart.component.ts b/ui/src/app/edge/history/peakshaving/timeslot/chart.component.ts index f933fec6d96..1e65baae7f0 100644 --- a/ui/src/app/edge/history/peakshaving/timeslot/chart.component.ts +++ b/ui/src/app/edge/history/peakshaving/timeslot/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/peakshaving/timeslot/timeslotpeakshavingchartoverview/timeslotpeakshavingchartoverview.component.ts b/ui/src/app/edge/history/peakshaving/timeslot/timeslotpeakshavingchartoverview/timeslotpeakshavingchartoverview.component.ts index f49c60691b1..79375034f28 100644 --- a/ui/src/app/edge/history/peakshaving/timeslot/timeslotpeakshavingchartoverview/timeslotpeakshavingchartoverview.component.ts +++ b/ui/src/app/edge/history/peakshaving/timeslot/timeslotpeakshavingchartoverview/timeslotpeakshavingchartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service } from '../../../../../shared/shared'; diff --git a/ui/src/app/edge/history/peakshaving/timeslot/widget.component.ts b/ui/src/app/edge/history/peakshaving/timeslot/widget.component.ts index c01df3e9348..ab34310c2a0 100644 --- a/ui/src/app/edge/history/peakshaving/timeslot/widget.component.ts +++ b/ui/src/app/edge/history/peakshaving/timeslot/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { DefaultTypes } from 'src/app/shared/service/defaulttypes'; diff --git a/ui/src/app/edge/history/shared.ts b/ui/src/app/edge/history/shared.ts index 9c9cddc5bf8..506dce92d5d 100644 --- a/ui/src/app/edge/history/shared.ts +++ b/ui/src/app/edge/history/shared.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import * as Chart from 'chart.js'; import { differenceInDays, differenceInMinutes, startOfDay } from 'date-fns'; import { de } from 'date-fns/locale'; @@ -318,7 +319,7 @@ export function calculateResolution(service: Service, fromDate: Date, toDate: Da if (days <= 1) { if (service.isSmartphoneResolution) { - result = { resolution: { value: 20, unit: ChronoUnit.Type.MINUTES }, timeFormat: 'hour' }; // 1 Day + result = { resolution: { value: 15, unit: ChronoUnit.Type.MINUTES }, timeFormat: 'hour' }; // 1 Day } else { result = { resolution: { value: 5, unit: ChronoUnit.Type.MINUTES }, timeFormat: 'hour' }; // 5 Minutes } diff --git a/ui/src/app/edge/history/singlethreshold/chart.component.ts b/ui/src/app/edge/history/singlethreshold/chart.component.ts index 32be29b928f..df56ee64199 100644 --- a/ui/src/app/edge/history/singlethreshold/chart.component.ts +++ b/ui/src/app/edge/history/singlethreshold/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { formatNumber } from '@angular/common'; import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; diff --git a/ui/src/app/edge/history/singlethreshold/singlethresholdchartoverview/singlethresholdchartoverview.component.ts b/ui/src/app/edge/history/singlethreshold/singlethresholdchartoverview/singlethresholdchartoverview.component.ts index 16ff4faef04..0c9fae32c4f 100644 --- a/ui/src/app/edge/history/singlethreshold/singlethresholdchartoverview/singlethresholdchartoverview.component.ts +++ b/ui/src/app/edge/history/singlethreshold/singlethresholdchartoverview/singlethresholdchartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service, Utils } from '../../../../shared/shared'; diff --git a/ui/src/app/edge/history/singlethreshold/widget.component.ts b/ui/src/app/edge/history/singlethreshold/widget.component.ts index 5a5b46ec2f1..5af56a066b8 100644 --- a/ui/src/app/edge/history/singlethreshold/widget.component.ts +++ b/ui/src/app/edge/history/singlethreshold/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { QueryHistoricTimeseriesDataResponse } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesDataResponse'; diff --git a/ui/src/app/edge/history/storage/chargerchart.component.ts b/ui/src/app/edge/history/storage/chargerchart.component.ts index 8b37ac0cb02..354e37e2aec 100644 --- a/ui/src/app/edge/history/storage/chargerchart.component.ts +++ b/ui/src/app/edge/history/storage/chargerchart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/storage/esschart.component.ts b/ui/src/app/edge/history/storage/esschart.component.ts index 0ad2c4f52b0..507377c51dd 100644 --- a/ui/src/app/edge/history/storage/esschart.component.ts +++ b/ui/src/app/edge/history/storage/esschart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/storage/singlechart.component.ts b/ui/src/app/edge/history/storage/singlechart.component.ts index e3ec04397ed..44bebe80fff 100644 --- a/ui/src/app/edge/history/storage/singlechart.component.ts +++ b/ui/src/app/edge/history/storage/singlechart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { formatNumber } from '@angular/common'; import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; diff --git a/ui/src/app/edge/history/storage/socchart.component.ts b/ui/src/app/edge/history/storage/socchart.component.ts index 55fac0feecb..b6e67531da2 100644 --- a/ui/src/app/edge/history/storage/socchart.component.ts +++ b/ui/src/app/edge/history/storage/socchart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/storage/storagechartoverview/storagechartoverview.component.ts b/ui/src/app/edge/history/storage/storagechartoverview/storagechartoverview.component.ts index 85eecd04639..1030838ca9c 100644 --- a/ui/src/app/edge/history/storage/storagechartoverview/storagechartoverview.component.ts +++ b/ui/src/app/edge/history/storage/storagechartoverview/storagechartoverview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Edge, EdgeConfig, Service, Utils } from '../../../../shared/shared'; diff --git a/ui/src/app/edge/history/storage/totalchart.component.ts b/ui/src/app/edge/history/storage/totalchart.component.ts index 0e7f761a8bc..36cfb326953 100644 --- a/ui/src/app/edge/history/storage/totalchart.component.ts +++ b/ui/src/app/edge/history/storage/totalchart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/history/storage/widget.component.ts b/ui/src/app/edge/history/storage/widget.component.ts index 3dcdcdcfed7..1673230f739 100644 --- a/ui/src/app/edge/history/storage/widget.component.ts +++ b/ui/src/app/edge/history/storage/widget.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Cumulated } from 'src/app/shared/jsonrpc/response/queryHistoricTimeseriesEnergyResponse'; diff --git a/ui/src/app/edge/live/Controller/Channelthreshold/Channelthreshold.ts b/ui/src/app/edge/live/Controller/Channelthreshold/Channelthreshold.ts index 10b64329b18..fe75017e03d 100644 --- a/ui/src/app/edge/live/Controller/Channelthreshold/Channelthreshold.ts +++ b/ui/src/app/edge/live/Controller/Channelthreshold/Channelthreshold.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { Icon } from 'src/app/shared/type/widget'; diff --git a/ui/src/app/edge/live/Controller/ChpSoc/ChpSoc.ts b/ui/src/app/edge/live/Controller/ChpSoc/ChpSoc.ts index b7fa89871de..76f7fc04222 100644 --- a/ui/src/app/edge/live/Controller/ChpSoc/ChpSoc.ts +++ b/ui/src/app/edge/live/Controller/ChpSoc/ChpSoc.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { Icon } from 'src/app/shared/type/widget'; diff --git a/ui/src/app/edge/live/Controller/ChpSoc/modal/modal.component.ts b/ui/src/app/edge/live/Controller/ChpSoc/modal/modal.component.ts index d84bc2e422d..3a4ab019f79 100644 --- a/ui/src/app/edge/live/Controller/ChpSoc/modal/modal.component.ts +++ b/ui/src/app/edge/live/Controller/ChpSoc/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/Controller/Ess/FixActivePower/Ess_FixActivePower.ts b/ui/src/app/edge/live/Controller/Ess/FixActivePower/Ess_FixActivePower.ts index 61c72138ad6..471d2ee863d 100644 --- a/ui/src/app/edge/live/Controller/Ess/FixActivePower/Ess_FixActivePower.ts +++ b/ui/src/app/edge/live/Controller/Ess/FixActivePower/Ess_FixActivePower.ts @@ -9,10 +9,6 @@ import { ModalComponent } from './modal/modal'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - ], declarations: [ FlatComponent, ModalComponent, diff --git a/ui/src/app/edge/live/Controller/Ess/FixActivePower/flat/flat.ts b/ui/src/app/edge/live/Controller/Ess/FixActivePower/flat/flat.ts index 44af2e7d70d..114bb581a71 100644 --- a/ui/src/app/edge/live/Controller/Ess/FixActivePower/flat/flat.ts +++ b/ui/src/app/edge/live/Controller/Ess/FixActivePower/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { DefaultTypes } from 'src/app/shared/service/defaulttypes'; diff --git a/ui/src/app/edge/live/Controller/Ess/FixActivePower/modal/modal.ts b/ui/src/app/edge/live/Controller/Ess/FixActivePower/modal/modal.ts index cd23b31bd62..82b9e73d33e 100644 --- a/ui/src/app/edge/live/Controller/Ess/FixActivePower/modal/modal.ts +++ b/ui/src/app/edge/live/Controller/Ess/FixActivePower/modal/modal.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { AbstractModal } from 'src/app/shared/genericComponents/modal/abstractModal'; diff --git a/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/Ess_GridOptimizedCharge.ts b/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/Ess_GridOptimizedCharge.ts index 72b7aaa5e06..90c1bb531b7 100644 --- a/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/Ess_GridOptimizedCharge.ts +++ b/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/Ess_GridOptimizedCharge.ts @@ -10,11 +10,6 @@ import { PredictionChartComponent } from './modal/predictionChart'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - PredictionChartComponent, - ], declarations: [ FlatComponent, ModalComponent, diff --git a/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/flat/flat.ts b/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/flat/flat.ts index 3248cc5dc65..daf5c9530ab 100644 --- a/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/flat/flat.ts +++ b/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, CurrentData, EdgeConfig, Utils } from 'src/app/shared/shared'; diff --git a/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/modal/modal.ts b/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/modal/modal.ts index 0184f951da7..703dc533b0e 100644 --- a/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/modal/modal.ts +++ b/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/modal/modal.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChangeDetectionStrategy, Component } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; import { AbstractModal } from 'src/app/shared/genericComponents/modal/abstractModal'; diff --git a/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/modal/predictionChart.ts b/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/modal/predictionChart.ts index 3c21c751a26..e785684e2b0 100644 --- a/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/modal/predictionChart.ts +++ b/ui/src/app/edge/live/Controller/Ess/GridOptimizedCharge/modal/predictionChart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/Ess_TimeOfUseTariff.ts b/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/Ess_TimeOfUseTariff.ts index 2baf5db81e0..3e068cbf248 100644 --- a/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/Ess_TimeOfUseTariff.ts +++ b/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/Ess_TimeOfUseTariff.ts @@ -1,5 +1,10 @@ +// @ts-strict-ignore import { NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; +import { TranslateService } from "@ngx-translate/core"; +import { ChartDataset } from "chart.js"; +import { ChartAxis, TimeOfUseTariffUtils } from "src/app/shared/service/utils"; +import { Utils } from "src/app/shared/shared"; import { SharedModule } from "src/app/shared/shared.module"; import { FlatComponent } from "./flat/flat"; import { ModalComponent } from "./modal/modal"; @@ -11,12 +16,6 @@ import { ScheduleStateAndPriceChartComponent } from "./modal/statePriceChart"; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - ScheduleStateAndPriceChartComponent, - SchedulePowerAndSocChartComponent, - ], declarations: [ FlatComponent, ModalComponent, @@ -28,4 +27,141 @@ import { ScheduleStateAndPriceChartComponent } from "./modal/statePriceChart"; ], }) export class Controller_Ess_TimeOfUseTariff { + + /** + * Gets the schedule chart data containing datasets, colors and labels. + * + * @param size The length of the dataset + * @param prices The Time-of-Use-Tariff quarterly price array + * @param states The Time-of-Use-Tariff state array + * @param timestamps The Time-of-Use-Tariff timestamps array + * @param gridBuy The Time-of-Use-Tariff gridBuy array + * @param socArray The Time-of0Use-Tariff soc Array. + * @param translate The Translate service + * @param controlMode The Control mode of the controller. + * @returns The ScheduleChartData. + */ + public static getScheduleChartData(size: number, prices: number[], states: number[], timestamps: string[], + gridBuy: number[], socArray: number[], translate: TranslateService, + controlMode: Controller_Ess_TimeOfUseTariff.ControlMode): Controller_Ess_TimeOfUseTariff.ScheduleChartData { + + const datasets: ChartDataset[] = []; + const colors: any[] = []; + const labels: Date[] = []; + + // Initializing States. + const barChargeGrid = Array(size).fill(null); + const barBalancing = Array(size).fill(null); + const barDelayDischarge = Array(size).fill(null); + + for (let index = 0; index < size; index++) { + const quarterlyPrice = TimeOfUseTariffUtils.formatPrice(prices[index]); + const state = states[index]; + labels.push(new Date(timestamps[index])); + + if (state !== null) { + switch (state) { + case TimeOfUseTariffUtils.State.DelayDischarge: + barDelayDischarge[index] = quarterlyPrice; + break; + case TimeOfUseTariffUtils.State.Balancing: + barBalancing[index] = quarterlyPrice; + break; + case TimeOfUseTariffUtils.State.ChargeGrid: + barChargeGrid[index] = quarterlyPrice; + break; + } + } + } + + // Set datasets + datasets.push({ + type: 'bar', + label: translate.instant('Edge.Index.Widgets.TIME_OF_USE_TARIFF.STATE.BALANCING'), + data: barBalancing, + order: 1, + }); + colors.push({ + // Dark Green + backgroundColor: 'rgba(51,102,0,0.8)', + borderColor: 'rgba(51,102,0,1)', + }); + + // Set dataset for ChargeGrid. + if (!barChargeGrid.every(v => v === null) || controlMode == Controller_Ess_TimeOfUseTariff.ControlMode.CHARGE_CONSUMPTION) { + datasets.push({ + type: 'bar', + label: translate.instant('Edge.Index.Widgets.TIME_OF_USE_TARIFF.STATE.CHARGE_GRID'), + data: barChargeGrid, + order: 1, + }); + colors.push({ + // Sky blue + backgroundColor: 'rgba(0, 204, 204,0.5)', + borderColor: 'rgba(0, 204, 204,0.7)', + }); + } + + // Set dataset for buy from grid + datasets.push({ + type: 'bar', + label: translate.instant('Edge.Index.Widgets.TIME_OF_USE_TARIFF.STATE.DELAY_DISCHARGE'), + data: barDelayDischarge, + order: 1, + }); + colors.push({ + // Black + backgroundColor: 'rgba(0,0,0,0.8)', + borderColor: 'rgba(0,0,0,0.9)', + }); + + // State of charge data + datasets.push({ + type: 'line', + label: translate.instant('General.soc'), + data: socArray, + hidden: false, + yAxisID: ChartAxis.RIGHT, + borderDash: [10, 10], + order: 0, + }); + colors.push({ + backgroundColor: 'rgba(189, 195, 199,0.2)', + borderColor: 'rgba(189, 195, 199,1)', + }); + + datasets.push({ + type: 'line', + label: translate.instant('General.gridBuy'), + data: gridBuy.map(v => Utils.divideSafely(v, 1000)), // [W] to [kW] + hidden: true, + yAxisID: ChartAxis.RIGHT_2, + order: 2, + }); + colors.push({ + backgroundColor: 'rgba(0,0,0, 0.2)', + borderColor: 'rgba(0,0,0, 1)', + }); + + const scheduleChartData: Controller_Ess_TimeOfUseTariff.ScheduleChartData = { + colors: colors, + datasets: datasets, + labels: labels, + }; + + return scheduleChartData; + } +} + +export namespace Controller_Ess_TimeOfUseTariff { + export type ScheduleChartData = { + datasets: ChartDataset[], + colors: any[], + labels: Date[] + }; + + export enum ControlMode { + CHARGE_CONSUMPTION = 'CHARGE_CONSUMPTION', + DELAY_DISCHARGE = 'DELAY_DISCHARGE' + } } diff --git a/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/modal.ts b/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/modal.ts index 5c5ba73e90a..368003307d2 100644 --- a/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/modal.ts +++ b/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/modal.ts @@ -1,8 +1,9 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { AbstractModal } from 'src/app/shared/genericComponents/modal/abstractModal'; -import { TimeOfUseTariffUtils } from 'src/app/shared/service/utils'; import { ChannelAddress, Currency, CurrentData } from 'src/app/shared/shared'; +import { Controller_Ess_TimeOfUseTariff } from '../Ess_TimeOfUseTariff'; @Component({ templateUrl: './modal.html', @@ -11,13 +12,12 @@ export class ModalComponent extends AbstractModal { protected readonly CONVERT_TIME_OF_USE_TARIFF_STATE = this.Utils.CONVERT_TIME_OF_USE_TARIFF_STATE(this.translate); protected priceWithCurrency: any; - protected controlMode = TimeOfUseTariffUtils.ControlMode; protected override getFormGroup(): FormGroup { return this.formBuilder.group({ mode: new FormControl(this.component.properties.mode), controlMode: new FormControl(this.component.properties.controlMode), - chargeConsumptionIsActive: new FormControl(this.component.properties.controlMode === this.controlMode.CHARGE_CONSUMPTION ? true : false), + chargeConsumptionIsActive: new FormControl(this.component.properties.controlMode === Controller_Ess_TimeOfUseTariff.ControlMode.CHARGE_CONSUMPTION ? true : false), }); } @@ -32,7 +32,9 @@ export class ModalComponent extends AbstractModal { this.formGroup?.get('chargeConsumptionIsActive') .valueChanges .subscribe(isActive => { - const controlMode: TimeOfUseTariffUtils.ControlMode = isActive ? this.controlMode.CHARGE_CONSUMPTION : this.controlMode.DELAY_DISCHARGE; + const controlMode: Controller_Ess_TimeOfUseTariff.ControlMode = isActive + ? Controller_Ess_TimeOfUseTariff.ControlMode.CHARGE_CONSUMPTION + : Controller_Ess_TimeOfUseTariff.ControlMode.DELAY_DISCHARGE; this.formGroup.controls['controlMode'].setValue(controlMode); this.formGroup.controls['controlMode'].markAsDirty(); })); diff --git a/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/powerSocChart.ts b/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/powerSocChart.ts index cf839777858..562a139ceae 100644 --- a/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/powerSocChart.ts +++ b/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/powerSocChart.ts @@ -1,12 +1,12 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import * as Chart from 'chart.js'; import { AbstractHistoryChart } from 'src/app/edge/history/abstracthistorychart'; import { AbstractHistoryChart as NewAbstractHistoryChart } from 'src/app/shared/genericComponents/chart/abstracthistorychart'; -import { ChartConstants } from 'src/app/shared/genericComponents/chart/chart.constants'; import { ComponentJsonApiRequest } from 'src/app/shared/jsonrpc/request/componentJsonApiRequest'; -import { ChartAxis, HistoryUtils, TimeOfUseTariffUtils, YAxisTitle } from 'src/app/shared/service/utils'; +import { ChartAxis, HistoryUtils, TimeOfUseTariffUtils, Utils, YAxisTitle } from 'src/app/shared/service/utils'; import { ChannelAddress, Edge, EdgeConfig, Service, Websocket } from 'src/app/shared/shared'; import { GetScheduleRequest } from '../../../../../../shared/jsonrpc/request/getScheduleRequest'; @@ -60,12 +60,12 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl // Extracting prices and states from the schedule array const { gridBuyArray, gridSellArray, productionArray, consumptionArray, essDischargeArray, essChargeArray, socArray, labels } = { - gridBuyArray: schedule.map(entry => HistoryUtils.ValueConverter.NEGATIVE_AS_ZERO(entry.grid)), - gridSellArray: schedule.map(entry => HistoryUtils.ValueConverter.POSITIVE_AS_ZERO_AND_INVERT_NEGATIVE(entry.grid)), - productionArray: schedule.map(entry => entry.production), - consumptionArray: schedule.map(entry => entry.consumption), - essDischargeArray: schedule.map(entry => HistoryUtils.ValueConverter.NEGATIVE_AS_ZERO(entry.ess)), - essChargeArray: schedule.map(entry => HistoryUtils.ValueConverter.POSITIVE_AS_ZERO_AND_INVERT_NEGATIVE(entry.ess)), + gridBuyArray: schedule.map(entry => HistoryUtils.ValueConverter.NEGATIVE_AS_ZERO(entry.grid), 1000), + gridSellArray: schedule.map(entry => HistoryUtils.ValueConverter.POSITIVE_AS_ZERO_AND_INVERT_NEGATIVE(entry.grid), 1000), + productionArray: schedule.map(entry => entry.production, 1000), + consumptionArray: schedule.map(entry => entry.consumption, 1000), + essDischargeArray: schedule.map(entry => HistoryUtils.ValueConverter.NEGATIVE_AS_ZERO(entry.ess), 1000), + essChargeArray: schedule.map(entry => HistoryUtils.ValueConverter.POSITIVE_AS_ZERO_AND_INVERT_NEGATIVE(entry.ess), 1000), socArray: schedule.map(entry => entry.soc), labels: schedule.map(entry => new Date(entry.timestamp)), }; @@ -73,7 +73,7 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl datasets.push({ type: 'line', label: this.translate.instant('General.gridBuy'), - data: gridBuyArray, + data: gridBuyArray.map(v => Utils.divideSafely(v, 1000)), // [W] to [kW] hidden: true, order: 1, }); @@ -85,7 +85,7 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl datasets.push({ type: 'line', label: this.translate.instant('General.gridSell'), - data: gridSellArray, + data: gridSellArray.map(v => Utils.divideSafely(v, 1000)), // [W] to [kW] hidden: true, order: 1, }); @@ -97,7 +97,7 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl datasets.push({ type: 'line', label: this.translate.instant('General.production'), - data: productionArray, + data: productionArray.map(v => Utils.divideSafely(v, 1000)), // [W] to [kW] hidden: false, order: 1, }); @@ -109,7 +109,7 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl datasets.push({ type: 'line', label: this.translate.instant('General.consumption'), - data: consumptionArray, + data: consumptionArray.map(v => Utils.divideSafely(v, 1000)), // [W] to [kW] hidden: false, order: 1, }); @@ -121,9 +121,10 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl datasets.push({ type: 'line', label: this.translate.instant('General.chargePower'), - data: essChargeArray, + data: essChargeArray.map(v => Utils.divideSafely(v, 1000)), // [W] to [kW] hidden: true, order: 1, + unit: YAxisTitle.POWER, }); this.colors.push({ backgroundColor: 'rgba(0,223,0, 0.2)', @@ -133,9 +134,10 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl datasets.push({ type: 'line', label: this.translate.instant('General.dischargePower'), - data: essDischargeArray, + data: essDischargeArray.map(v => Utils.divideSafely(v, 1000)), // [W] to [kW] hidden: true, order: 1, + unit: YAxisTitle.POWER, }); this.colors.push({ backgroundColor: 'rgba(200,0,0, 0.2)', @@ -168,7 +170,6 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl this.initializeChart(); return; }).finally(async () => { - this.unit = YAxisTitle.POWER; await this.setOptions(this.options); this.applyControllerSpecificOptions(); }); @@ -176,11 +177,10 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl private applyControllerSpecificOptions() { const rightYAxis: HistoryUtils.yAxes = { position: 'right', unit: YAxisTitle.PERCENTAGE, yAxisId: ChartAxis.RIGHT }; - const leftYAxis: HistoryUtils.yAxes = { position: 'left', unit: YAxisTitle.ENERGY, yAxisId: ChartAxis.LEFT }; + const leftYAxis: HistoryUtils.yAxes = { position: 'left', unit: YAxisTitle.POWER, yAxisId: ChartAxis.LEFT }; const locale = this.service.translate.currentLang; - const scaleOptionsLeft = ChartConstants.getScaleOptions(this.datasets, leftYAxis); - this.options = NewAbstractHistoryChart.getYAxisOptions(this.options, rightYAxis, this.translate, 'line', locale, true, scaleOptionsLeft); + this.options = NewAbstractHistoryChart.getYAxisOptions(this.options, rightYAxis, this.translate, 'line', locale, true); this.options = NewAbstractHistoryChart.getYAxisOptions(this.options, leftYAxis, this.translate, 'line', locale, true); this.datasets = this.datasets.map((el: Chart.ChartDataset) => { @@ -201,6 +201,8 @@ export class SchedulePowerAndSocChartComponent extends AbstractHistoryChart impl }; this.options.scales[ChartAxis.RIGHT].grid.display = false; + this.options.scales[ChartAxis.LEFT].suggestedMin = 0; + this.options.scales[ChartAxis.LEFT].suggestedMax = 1; } protected setLabel() { diff --git a/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/statePriceChart.ts b/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/statePriceChart.ts index 3edc5233d72..cb6fc333a81 100644 --- a/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/statePriceChart.ts +++ b/ui/src/app/edge/live/Controller/Ess/TimeOfUseTariff/modal/statePriceChart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; @@ -12,6 +13,7 @@ import { calculateResolution } from 'src/app/edge/history/shared'; import { ColorUtils } from 'src/app/shared/utils/color/color.utils'; import { GetScheduleRequest } from '../../../../../../shared/jsonrpc/request/getScheduleRequest'; import { GetScheduleResponse } from '../../../../../../shared/jsonrpc/response/getScheduleResponse'; +import { Controller_Ess_TimeOfUseTariff } from '../Ess_TimeOfUseTariff'; @Component({ selector: 'statePriceChart', @@ -69,7 +71,8 @@ export class ScheduleStateAndPriceChartComponent extends AbstractHistoryChart im socArray: schedule.map(entry => entry.soc), }; - const scheduleChartData = TimeOfUseTariffUtils.getScheduleChartData(schedule.length, priceArray, stateArray, timestampArray, gridBuyArray, socArray, this.translate, this.component.properties.controlMode); + const scheduleChartData = Controller_Ess_TimeOfUseTariff.getScheduleChartData(schedule.length, priceArray, + stateArray, timestampArray, gridBuyArray, socArray, this.translate, this.component.properties.controlMode); this.colors = scheduleChartData.colors; this.labels = scheduleChartData.labels; @@ -78,10 +81,12 @@ export class ScheduleStateAndPriceChartComponent extends AbstractHistoryChart im this.loading = false; this.setLabel(); this.stopSpinner(); + }).catch((reason) => { console.error(reason); this.initializeChart(); return; + }).finally(async () => { this.unit = YAxisTitle.CURRENCY; await this.setOptions(this.options); @@ -152,6 +157,8 @@ export class ScheduleStateAndPriceChartComponent extends AbstractHistoryChart im this.options.scales[ChartAxis.LEFT]['title'].text = this.currencyLabel; this.options.scales[ChartAxis.RIGHT].grid.display = false; + this.options.scales[ChartAxis.RIGHT_2].suggestedMin = 0; + this.options.scales[ChartAxis.RIGHT_2].suggestedMax = 1; this.options.scales[ChartAxis.RIGHT_2].grid.display = false; this.options['animation'] = false; } diff --git a/ui/src/app/edge/live/Controller/Evcs/Evcs.ts b/ui/src/app/edge/live/Controller/Evcs/Evcs.ts index 21dec28b819..6a2568a03c5 100644 --- a/ui/src/app/edge/live/Controller/Evcs/Evcs.ts +++ b/ui/src/app/edge/live/Controller/Evcs/Evcs.ts @@ -10,11 +10,6 @@ import { PopoverComponent } from './popover/popover'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - PopoverComponent, - ], declarations: [ FlatComponent, ModalComponent, diff --git a/ui/src/app/edge/live/Controller/Evcs/administration/administration.component.ts b/ui/src/app/edge/live/Controller/Evcs/administration/administration.component.ts index e1ffd2103e3..bc9fb3b4fef 100644 --- a/ui/src/app/edge/live/Controller/Evcs/administration/administration.component.ts +++ b/ui/src/app/edge/live/Controller/Evcs/administration/administration.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/Controller/Evcs/flat/flat.ts b/ui/src/app/edge/live/Controller/Evcs/flat/flat.ts index ff43a24e529..50cb8fede3e 100644 --- a/ui/src/app/edge/live/Controller/Evcs/flat/flat.ts +++ b/ui/src/app/edge/live/Controller/Evcs/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { DefaultTypes } from 'src/app/shared/service/defaulttypes'; diff --git a/ui/src/app/edge/live/Controller/Evcs/modal/modal.ts b/ui/src/app/edge/live/Controller/Evcs/modal/modal.ts index 2a394e903e1..3971ecd4e94 100644 --- a/ui/src/app/edge/live/Controller/Evcs/modal/modal.ts +++ b/ui/src/app/edge/live/Controller/Evcs/modal/modal.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChangeDetectorRef, Component, Inject } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; diff --git a/ui/src/app/edge/live/Controller/Evcs/popover/popover.ts b/ui/src/app/edge/live/Controller/Evcs/popover/popover.ts index c2272df7af1..94f6f39109a 100644 --- a/ui/src/app/edge/live/Controller/Evcs/popover/popover.ts +++ b/ui/src/app/edge/live/Controller/Evcs/popover/popover.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractModal } from 'src/app/shared/genericComponents/modal/abstractModal'; diff --git a/ui/src/app/edge/live/Controller/Io/ChannelSingleThreshold/Io_ChannelSingleThreshold.ts b/ui/src/app/edge/live/Controller/Io/ChannelSingleThreshold/Io_ChannelSingleThreshold.ts index 450cd89fc59..df989ea5c05 100644 --- a/ui/src/app/edge/live/Controller/Io/ChannelSingleThreshold/Io_ChannelSingleThreshold.ts +++ b/ui/src/app/edge/live/Controller/Io/ChannelSingleThreshold/Io_ChannelSingleThreshold.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, CurrentData, Utils } from 'src/app/shared/shared'; diff --git a/ui/src/app/edge/live/Controller/Io/ChannelSingleThreshold/modal/modal.component.ts b/ui/src/app/edge/live/Controller/Io/ChannelSingleThreshold/modal/modal.component.ts index 05f2e79bb9c..68a871803e4 100644 --- a/ui/src/app/edge/live/Controller/Io/ChannelSingleThreshold/modal/modal.component.ts +++ b/ui/src/app/edge/live/Controller/Io/ChannelSingleThreshold/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/Controller/Io/FixDigitalOutput/Io_FixDigitalOutput.ts b/ui/src/app/edge/live/Controller/Io/FixDigitalOutput/Io_FixDigitalOutput.ts index fdaf6d1e271..80c7c667c3f 100644 --- a/ui/src/app/edge/live/Controller/Io/FixDigitalOutput/Io_FixDigitalOutput.ts +++ b/ui/src/app/edge/live/Controller/Io/FixDigitalOutput/Io_FixDigitalOutput.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, CurrentData } from 'src/app/shared/shared'; diff --git a/ui/src/app/edge/live/Controller/Io/FixDigitalOutput/modal/modal.component.ts b/ui/src/app/edge/live/Controller/Io/FixDigitalOutput/modal/modal.component.ts index aa8c5ece5fd..3a6111ccab2 100644 --- a/ui/src/app/edge/live/Controller/Io/FixDigitalOutput/modal/modal.component.ts +++ b/ui/src/app/edge/live/Controller/Io/FixDigitalOutput/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from '@angular/core'; import { Router } from '@angular/router'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/Controller/Io/HeatingElement/Io_HeatingElement.ts b/ui/src/app/edge/live/Controller/Io/HeatingElement/Io_HeatingElement.ts index 7c4f9b30984..d390623b36b 100644 --- a/ui/src/app/edge/live/Controller/Io/HeatingElement/Io_HeatingElement.ts +++ b/ui/src/app/edge/live/Controller/Io/HeatingElement/Io_HeatingElement.ts @@ -9,10 +9,6 @@ import { ModalComponent } from "./modal/modal"; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - ], declarations: [ FlatComponent, ModalComponent, diff --git a/ui/src/app/edge/live/Controller/Io/HeatingElement/flat/flat.ts b/ui/src/app/edge/live/Controller/Io/HeatingElement/flat/flat.ts index afc1d046a05..ac081a96468 100644 --- a/ui/src/app/edge/live/Controller/Io/HeatingElement/flat/flat.ts +++ b/ui/src/app/edge/live/Controller/Io/HeatingElement/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/live/Controller/Io/HeatingElement/modal/modal.ts b/ui/src/app/edge/live/Controller/Io/HeatingElement/modal/modal.ts index 35a0e239d55..c440f01a45e 100644 --- a/ui/src/app/edge/live/Controller/Io/HeatingElement/modal/modal.ts +++ b/ui/src/app/edge/live/Controller/Io/HeatingElement/modal/modal.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { FormControl, FormGroup } from '@angular/forms'; import { BehaviorSubject } from 'rxjs'; diff --git a/ui/src/app/edge/live/Controller/Io/Heatpump/Io_Heatpump.ts b/ui/src/app/edge/live/Controller/Io/Heatpump/Io_Heatpump.ts index a9487152f25..7fbbc30b59d 100644 --- a/ui/src/app/edge/live/Controller/Io/Heatpump/Io_Heatpump.ts +++ b/ui/src/app/edge/live/Controller/Io/Heatpump/Io_Heatpump.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/live/Controller/Io/Heatpump/modal/modal.component.ts b/ui/src/app/edge/live/Controller/Io/Heatpump/modal/modal.component.ts index 5d7e1874d9a..367e192854f 100644 --- a/ui/src/app/edge/live/Controller/Io/Heatpump/modal/modal.component.ts +++ b/ui/src/app/edge/live/Controller/Io/Heatpump/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/Controller/PeakShaving/Asymmetric/Asymmetric.ts b/ui/src/app/edge/live/Controller/PeakShaving/Asymmetric/Asymmetric.ts index 08c2f7cfc1c..c1afbe5d598 100644 --- a/ui/src/app/edge/live/Controller/PeakShaving/Asymmetric/Asymmetric.ts +++ b/ui/src/app/edge/live/Controller/PeakShaving/Asymmetric/Asymmetric.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/live/Controller/PeakShaving/Asymmetric/modal/modal.component.ts b/ui/src/app/edge/live/Controller/PeakShaving/Asymmetric/modal/modal.component.ts index 68988b5cf20..751df3e54b1 100644 --- a/ui/src/app/edge/live/Controller/PeakShaving/Asymmetric/modal/modal.component.ts +++ b/ui/src/app/edge/live/Controller/PeakShaving/Asymmetric/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/Controller/PeakShaving/Symmetric/Symmetric.ts b/ui/src/app/edge/live/Controller/PeakShaving/Symmetric/Symmetric.ts index 36b337fdc48..1d25869f2bd 100644 --- a/ui/src/app/edge/live/Controller/PeakShaving/Symmetric/Symmetric.ts +++ b/ui/src/app/edge/live/Controller/PeakShaving/Symmetric/Symmetric.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/live/Controller/PeakShaving/Symmetric/modal/modal.component.ts b/ui/src/app/edge/live/Controller/PeakShaving/Symmetric/modal/modal.component.ts index e53c0cc60c5..2f1c86eed96 100644 --- a/ui/src/app/edge/live/Controller/PeakShaving/Symmetric/modal/modal.component.ts +++ b/ui/src/app/edge/live/Controller/PeakShaving/Symmetric/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/Controller/PeakShaving/Symmetric_TimeSlot/Symmetric_TimeSlot.ts b/ui/src/app/edge/live/Controller/PeakShaving/Symmetric_TimeSlot/Symmetric_TimeSlot.ts index d702d4bf962..e8d9e356b7b 100644 --- a/ui/src/app/edge/live/Controller/PeakShaving/Symmetric_TimeSlot/Symmetric_TimeSlot.ts +++ b/ui/src/app/edge/live/Controller/PeakShaving/Symmetric_TimeSlot/Symmetric_TimeSlot.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/live/Controller/PeakShaving/Symmetric_TimeSlot/modal/modal.component.ts b/ui/src/app/edge/live/Controller/PeakShaving/Symmetric_TimeSlot/modal/modal.component.ts index ce717a33a0b..a424dd6df36 100644 --- a/ui/src/app/edge/live/Controller/PeakShaving/Symmetric_TimeSlot/modal/modal.component.ts +++ b/ui/src/app/edge/live/Controller/PeakShaving/Symmetric_TimeSlot/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/Io/Api_DigitalInput/Io_Api_DigitalInput.ts b/ui/src/app/edge/live/Io/Api_DigitalInput/Io_Api_DigitalInput.ts index c0980557c66..0fdc12f4202 100644 --- a/ui/src/app/edge/live/Io/Api_DigitalInput/Io_Api_DigitalInput.ts +++ b/ui/src/app/edge/live/Io/Api_DigitalInput/Io_Api_DigitalInput.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, EdgeConfig } from 'src/app/shared/shared'; diff --git a/ui/src/app/edge/live/Io/Api_DigitalInput/modal/modal.component.ts b/ui/src/app/edge/live/Io/Api_DigitalInput/modal/modal.component.ts index eef0fc31745..2402bb3ea24 100644 --- a/ui/src/app/edge/live/Io/Api_DigitalInput/modal/modal.component.ts +++ b/ui/src/app/edge/live/Io/Api_DigitalInput/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from '@angular/core'; import { Edge, Service, EdgeConfig } from '../../../../../shared/shared'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/Evcs_Api_Cluster.ts b/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/Evcs_Api_Cluster.ts index 4b4bfa0d57f..43f8fe3fe25 100644 --- a/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/Evcs_Api_Cluster.ts +++ b/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/Evcs_Api_Cluster.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/modal/evcs-chart/evcs.chart.ts b/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/modal/evcs-chart/evcs.chart.ts index ad1ce0727d7..e78268eda98 100644 --- a/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/modal/evcs-chart/evcs.chart.ts +++ b/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/modal/evcs-chart/evcs.chart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import * as Chart from 'chart.js'; import { Component, Input, OnInit, OnChanges } from '@angular/core'; import { CurrentData } from 'src/app/shared/edge/currentdata'; diff --git a/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/modal/evcsCluster-modal.page.ts b/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/modal/evcsCluster-modal.page.ts index 2ae12fc26fe..f1f6edc62d6 100644 --- a/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/modal/evcsCluster-modal.page.ts +++ b/ui/src/app/edge/live/Multiple/Evcs_Api_Cluster/modal/evcsCluster-modal.page.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ActivatedRoute, Router } from '@angular/router'; import { Component, Input, OnInit, ViewChild } from '@angular/core'; import { Edge, EdgeConfig, Service, Websocket } from 'src/app/shared/shared'; diff --git a/ui/src/app/edge/live/common/autarchy/Common_Autarchy.ts b/ui/src/app/edge/live/common/autarchy/Common_Autarchy.ts index 3360cef6144..8aa0d3022b4 100644 --- a/ui/src/app/edge/live/common/autarchy/Common_Autarchy.ts +++ b/ui/src/app/edge/live/common/autarchy/Common_Autarchy.ts @@ -9,10 +9,6 @@ import { ModalComponent } from './modal/modal'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - ], declarations: [ FlatComponent, ModalComponent, diff --git a/ui/src/app/edge/live/common/autarchy/flat/flat.ts b/ui/src/app/edge/live/common/autarchy/flat/flat.ts index baa38c8bfce..cc161dbc2d9 100644 --- a/ui/src/app/edge/live/common/autarchy/flat/flat.ts +++ b/ui/src/app/edge/live/common/autarchy/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, CurrentData, Utils } from 'src/app/shared/shared'; diff --git a/ui/src/app/edge/live/common/consumption/Common_Consumption.ts b/ui/src/app/edge/live/common/consumption/Common_Consumption.ts index 5059a7e0ce8..f685ef859cf 100644 --- a/ui/src/app/edge/live/common/consumption/Common_Consumption.ts +++ b/ui/src/app/edge/live/common/consumption/Common_Consumption.ts @@ -9,10 +9,6 @@ import { ModalComponent } from './modal/modal'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - ], declarations: [ FlatComponent, ModalComponent, diff --git a/ui/src/app/edge/live/common/consumption/flat/flat.ts b/ui/src/app/edge/live/common/consumption/flat/flat.ts index 8170a24be7d..34f7f9f16a8 100644 --- a/ui/src/app/edge/live/common/consumption/flat/flat.ts +++ b/ui/src/app/edge/live/common/consumption/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, CurrentData, EdgeConfig, Utils } from 'src/app/shared/shared'; diff --git a/ui/src/app/edge/live/common/consumption/modal/modal.spec.ts b/ui/src/app/edge/live/common/consumption/modal/modal.spec.ts index a93aed2185b..eb9eae845eb 100644 --- a/ui/src/app/edge/live/common/consumption/modal/modal.spec.ts +++ b/ui/src/app/edge/live/common/consumption/modal/modal.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { CHANNEL_LINE, DummyConfig, LINE_HORIZONTAL, LINE_INFO_PHASES_DE, VALUE_FROM_CHANNELS_LINE } from "src/app/shared/edge/edgeconfig.spec"; import { TextIndentation } from "src/app/shared/genericComponents/modal/modal-line/modal-line"; import { OeFormlyViewTester } from "src/app/shared/genericComponents/shared/testing/tester"; diff --git a/ui/src/app/edge/live/common/grid/Common_Grid.ts b/ui/src/app/edge/live/common/grid/Common_Grid.ts index f18c9384061..b560e4725d4 100644 --- a/ui/src/app/edge/live/common/grid/Common_Grid.ts +++ b/ui/src/app/edge/live/common/grid/Common_Grid.ts @@ -9,10 +9,6 @@ import { ModalComponent } from './modal/modal'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - ], declarations: [ FlatComponent, ModalComponent, diff --git a/ui/src/app/edge/live/common/grid/flat/flat.ts b/ui/src/app/edge/live/common/grid/flat/flat.ts index c1a86e14098..047830f4b9f 100644 --- a/ui/src/app/edge/live/common/grid/flat/flat.ts +++ b/ui/src/app/edge/live/common/grid/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { ChannelAddress, CurrentData, GridMode, Utils } from 'src/app/shared/shared'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/live/common/production/Common_Production.ts b/ui/src/app/edge/live/common/production/Common_Production.ts index 944697e272d..ab5942cf712 100644 --- a/ui/src/app/edge/live/common/production/Common_Production.ts +++ b/ui/src/app/edge/live/common/production/Common_Production.ts @@ -9,10 +9,6 @@ import { ModalComponent } from './modal/modal'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - ], declarations: [ FlatComponent, ModalComponent, diff --git a/ui/src/app/edge/live/common/selfconsumption/Common_Selfconsumption.ts b/ui/src/app/edge/live/common/selfconsumption/Common_Selfconsumption.ts index 846a5e07e49..6c49f7603a4 100644 --- a/ui/src/app/edge/live/common/selfconsumption/Common_Selfconsumption.ts +++ b/ui/src/app/edge/live/common/selfconsumption/Common_Selfconsumption.ts @@ -9,10 +9,6 @@ import { ModalComponent } from './modal/modal'; BrowserModule, SharedModule, ], - entryComponents: [ - FlatComponent, - ModalComponent, - ], declarations: [ FlatComponent, ModalComponent, diff --git a/ui/src/app/edge/live/common/selfconsumption/flat/flat.ts b/ui/src/app/edge/live/common/selfconsumption/flat/flat.ts index 8a7223e3bff..73f504b17de 100644 --- a/ui/src/app/edge/live/common/selfconsumption/flat/flat.ts +++ b/ui/src/app/edge/live/common/selfconsumption/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; import { ChannelAddress, CurrentData, Utils } from 'src/app/shared/shared'; diff --git a/ui/src/app/edge/live/common/storage/modal/modal.component.ts b/ui/src/app/edge/live/common/storage/modal/modal.component.ts index 035eb139a8d..8fc10a04131 100644 --- a/ui/src/app/edge/live/common/storage/modal/modal.component.ts +++ b/ui/src/app/edge/live/common/storage/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/common/storage/storage.component.ts b/ui/src/app/edge/live/common/storage/storage.component.ts index f0ee2852496..f5558444284 100644 --- a/ui/src/app/edge/live/common/storage/storage.component.ts +++ b/ui/src/app/edge/live/common/storage/storage.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { formatNumber } from '@angular/common'; import { Component } from '@angular/core'; import { AbstractFlatWidget } from 'src/app/shared/genericComponents/flat/abstract-flat-widget'; diff --git a/ui/src/app/edge/live/delayedselltogrid/delayedselltogrid.component.ts b/ui/src/app/edge/live/delayedselltogrid/delayedselltogrid.component.ts index 166b0fbc080..5f14f0149a4 100644 --- a/ui/src/app/edge/live/delayedselltogrid/delayedselltogrid.component.ts +++ b/ui/src/app/edge/live/delayedselltogrid/delayedselltogrid.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/delayedselltogrid/modal/modal.component.ts b/ui/src/app/edge/live/delayedselltogrid/modal/modal.component.ts index b03668fc30c..c9a4efbb5b5 100644 --- a/ui/src/app/edge/live/delayedselltogrid/modal/modal.component.ts +++ b/ui/src/app/edge/live/delayedselltogrid/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/live/energymonitor/chart/chart.component.ts b/ui/src/app/edge/live/energymonitor/chart/chart.component.ts index 25ad7177fab..be5fe2c85d0 100644 --- a/ui/src/app/edge/live/energymonitor/chart/chart.component.ts +++ b/ui/src/app/edge/live/energymonitor/chart/chart.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { fromEvent, Subject } from 'rxjs'; import { debounceTime, delay, takeUntil } from 'rxjs/operators'; diff --git a/ui/src/app/edge/live/energymonitor/chart/section/abstractsection.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/abstractsection.component.ts index 3625b51d42b..95045c5cd53 100644 --- a/ui/src/app/edge/live/energymonitor/chart/section/abstractsection.component.ts +++ b/ui/src/app/edge/live/energymonitor/chart/section/abstractsection.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { DefaultTypes } from '../../../../../shared/service/defaulttypes'; import { Service } from 'src/app/shared/shared'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/live/energymonitor/chart/section/consumption.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/consumption.component.ts index f437f59fb88..5fc5be23e8c 100644 --- a/ui/src/app/edge/live/energymonitor/chart/section/consumption.component.ts +++ b/ui/src/app/edge/live/energymonitor/chart/section/consumption.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { animate, state, style, transition, trigger } from '@angular/animations'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/live/energymonitor/chart/section/grid.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/grid.component.ts index f02cef40162..a4ddb265ebc 100644 --- a/ui/src/app/edge/live/energymonitor/chart/section/grid.component.ts +++ b/ui/src/app/edge/live/energymonitor/chart/section/grid.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { animate, state, style, transition, trigger } from '@angular/animations'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/live/energymonitor/chart/section/production.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/production.component.ts index becd9c8b94a..655204788d6 100644 --- a/ui/src/app/edge/live/energymonitor/chart/section/production.component.ts +++ b/ui/src/app/edge/live/energymonitor/chart/section/production.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { animate, state, style, transition, trigger } from '@angular/animations'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/live/energymonitor/chart/section/storage.component.ts b/ui/src/app/edge/live/energymonitor/chart/section/storage.component.ts index 12e8f547495..0391ff3990e 100644 --- a/ui/src/app/edge/live/energymonitor/chart/section/storage.component.ts +++ b/ui/src/app/edge/live/energymonitor/chart/section/storage.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { animate, state, style, transition, trigger } from '@angular/animations'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/live/energymonitor/energymonitor.component.ts b/ui/src/app/edge/live/energymonitor/energymonitor.component.ts index 65521679073..ef8122fb281 100644 --- a/ui/src/app/edge/live/energymonitor/energymonitor.component.ts +++ b/ui/src/app/edge/live/energymonitor/energymonitor.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ChannelAddress, Edge, Service, Websocket } from '../../../shared/shared'; diff --git a/ui/src/app/edge/live/live.component.ts b/ui/src/app/edge/live/live.component.ts index 15c1ccafd52..8f36b79cb9f 100644 --- a/ui/src/app/edge/live/live.component.ts +++ b/ui/src/app/edge/live/live.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subject } from 'rxjs'; diff --git a/ui/src/app/edge/live/live.module.ts b/ui/src/app/edge/live/live.module.ts index d07a7dec8d8..62c2a8b672e 100644 --- a/ui/src/app/edge/live/live.module.ts +++ b/ui/src/app/edge/live/live.module.ts @@ -61,24 +61,6 @@ import { Controller_Evcs } from './Controller/Evcs/Evcs'; Controller_Evcs, Controller_Ess_TimeOfUseTariff, ], - entryComponents: [ - AdministrationComponent, - Controller_Asymmetric_PeakShavingModalComponent, - Controller_ChpSocComponent, - Controller_ChpSocModalComponent, - Controller_Io_ChannelSingleThresholdComponent, - Controller_Io_ChannelSingleThresholdModalComponent, - Controller_Io_FixDigitalOutputComponent, - Controller_Io_FixDigitalOutputModalComponent, - Controller_Io_HeatpumpModalComponent, - Controller_Symmetric_PeakShavingComponent, - Controller_Symmetric_TimeSlot_PeakShavingModalComponent, - DelayedSellToGridModalComponent, - Evcs_Api_ClusterModalComponent, - Io_Api_DigitalInput_ModalComponent, - Io_Api_DigitalInputComponent, - StorageModalComponent, - ], declarations: [ AdministrationComponent, Controller_Asymmetric_PeakShavingComponent, diff --git a/ui/src/app/edge/live/livedataservice.ts b/ui/src/app/edge/live/livedataservice.ts index 82f9578245b..6dd0c30ddbe 100644 --- a/ui/src/app/edge/live/livedataservice.ts +++ b/ui/src/app/edge/live/livedataservice.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Directive, Inject, OnDestroy } from "@angular/core"; import { takeUntil } from "rxjs/operators"; import { v4 as uuidv4 } from 'uuid'; diff --git a/ui/src/app/edge/settings/alerting/alerting.component.ts b/ui/src/app/edge/settings/alerting/alerting.component.ts index 62b1a96f77c..cf41c78d8a5 100644 --- a/ui/src/app/edge/settings/alerting/alerting.component.ts +++ b/ui/src/app/edge/settings/alerting/alerting.component.ts @@ -1,11 +1,11 @@ -import { User } from 'src/app/shared/jsonrpc/shared'; +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; -import { SetUserAlertingConfigsRequest, UserSettingRequest } from 'src/app/shared/jsonrpc/request/setUserAlertingConfigsRequest'; import { GetUserAlertingConfigsRequest } from 'src/app/shared/jsonrpc/request/getUserAlertingConfigsRequest'; -import { GetUserAlertingConfigsResponse, AlertingSettingResponse } from 'src/app/shared/jsonrpc/response/getUserAlertingConfigsResponse'; +import { SetUserAlertingConfigsRequest, UserSettingRequest } from 'src/app/shared/jsonrpc/request/setUserAlertingConfigsRequest'; +import { AlertingSettingResponse, GetUserAlertingConfigsResponse } from 'src/app/shared/jsonrpc/response/getUserAlertingConfigsResponse'; import { Edge, Service, Utils, Websocket } from 'src/app/shared/shared'; export enum AlertingType { @@ -32,7 +32,6 @@ export class AlertingComponent implements OnInit { protected readonly defaultValues: DefaultValues; protected edge: Edge; - protected user: User; protected error: Error; protected currentUserInformation: DetailedAlertingSetting; @@ -60,10 +59,6 @@ export class AlertingComponent implements OnInit { this.service.setCurrentComponent({ languageKey: 'Edge.Config.Index.alerting' }, this.route).then(edge => { this.edge = edge; - this.service.metadata.subscribe(metadata => { - this.user = metadata.user; - }); - const request = new GetUserAlertingConfigsRequest({ edgeId: this.edge.id }); this.sendRequest(request).then(response => { diff --git a/ui/src/app/edge/settings/app/app.module.ts b/ui/src/app/edge/settings/app/app.module.ts index 0a46bb809d3..3b12184a7ed 100644 --- a/ui/src/app/edge/settings/app/app.module.ts +++ b/ui/src/app/edge/settings/app/app.module.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { NgModule } from '@angular/core'; import { SharedModule } from 'src/app/shared/shared.module'; import { InstallAppComponent } from './install.component'; diff --git a/ui/src/app/edge/settings/app/formly/option-group-picker/formly-option-group-picker.component.ts b/ui/src/app/edge/settings/app/formly/option-group-picker/formly-option-group-picker.component.ts index ca942bdc65c..0141269e76d 100644 --- a/ui/src/app/edge/settings/app/formly/option-group-picker/formly-option-group-picker.component.ts +++ b/ui/src/app/edge/settings/app/formly/option-group-picker/formly-option-group-picker.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from "@angular/core"; import { FieldType, FieldTypeConfig, FormlyFieldConfig } from "@ngx-formly/core"; import { OptionGroup, OptionGroupConfig, Option, getTitleFromOptionConfig } from "./optionGroupPickerConfiguration"; diff --git a/ui/src/app/edge/settings/app/formly/reorder-select/formly-reorder-array.component.ts b/ui/src/app/edge/settings/app/formly/reorder-select/formly-reorder-array.component.ts index fd328a69a62..04cb6f3138b 100644 --- a/ui/src/app/edge/settings/app/formly/reorder-select/formly-reorder-array.component.ts +++ b/ui/src/app/edge/settings/app/formly/reorder-select/formly-reorder-array.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from "@angular/core"; import { ItemReorderEventDetail } from "@ionic/angular"; import { FieldType, FieldTypeConfig, FormlyFieldConfig, FormlyFieldProps } from "@ngx-formly/core"; diff --git a/ui/src/app/edge/settings/app/formly/safe-input/formly-safe-input-modal.component.ts b/ui/src/app/edge/settings/app/formly/safe-input/formly-safe-input-modal.component.ts index b1396ed9d1c..745dcf43183 100644 --- a/ui/src/app/edge/settings/app/formly/safe-input/formly-safe-input-modal.component.ts +++ b/ui/src/app/edge/settings/app/formly/safe-input/formly-safe-input-modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from "@angular/core"; import { FormGroup } from "@angular/forms"; import { ModalController } from "@ionic/angular"; diff --git a/ui/src/app/edge/settings/app/formly/safe-input/formly-safe-input.extended.ts b/ui/src/app/edge/settings/app/formly/safe-input/formly-safe-input.extended.ts index af18e0ba539..4470534d432 100644 --- a/ui/src/app/edge/settings/app/formly/safe-input/formly-safe-input.extended.ts +++ b/ui/src/app/edge/settings/app/formly/safe-input/formly-safe-input.extended.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from "@angular/core"; import { ModalController } from "@ionic/angular"; import { FieldWrapper, FormlyFieldConfig } from "@ngx-formly/core"; diff --git a/ui/src/app/edge/settings/app/index.component.ts b/ui/src/app/edge/settings/app/index.component.ts index e66a84813d1..b29368b81af 100644 --- a/ui/src/app/edge/settings/app/index.component.ts +++ b/ui/src/app/edge/settings/app/index.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { IonPopover, ModalController } from '@ionic/angular'; diff --git a/ui/src/app/edge/settings/app/install.component.ts b/ui/src/app/edge/settings/app/install.component.ts index 6734b5ead43..53a719c8218 100644 --- a/ui/src/app/edge/settings/app/install.component.ts +++ b/ui/src/app/edge/settings/app/install.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnDestroy, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; diff --git a/ui/src/app/edge/settings/app/jsonrpc/getAppAssistant.spec.ts b/ui/src/app/edge/settings/app/jsonrpc/getAppAssistant.spec.ts index 6f008936224..5e76d319812 100644 --- a/ui/src/app/edge/settings/app/jsonrpc/getAppAssistant.spec.ts +++ b/ui/src/app/edge/settings/app/jsonrpc/getAppAssistant.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { GetAppAssistant } from "./getAppAssistant"; import { FormlyFieldConfig } from "@ngx-formly/core"; diff --git a/ui/src/app/edge/settings/app/jsonrpc/getAppAssistant.ts b/ui/src/app/edge/settings/app/jsonrpc/getAppAssistant.ts index d3c6e1777ff..1e8088be0b9 100644 --- a/ui/src/app/edge/settings/app/jsonrpc/getAppAssistant.ts +++ b/ui/src/app/edge/settings/app/jsonrpc/getAppAssistant.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { AbstractControl } from "@angular/forms"; import { FormlyFieldConfig } from "@ngx-formly/core"; diff --git a/ui/src/app/edge/settings/app/keypopup/modal.component.ts b/ui/src/app/edge/settings/app/keypopup/modal.component.ts index e79bed2646d..d94779ef5c8 100644 --- a/ui/src/app/edge/settings/app/keypopup/modal.component.ts +++ b/ui/src/app/edge/settings/app/keypopup/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { Router } from '@angular/router'; diff --git a/ui/src/app/edge/settings/app/single.component.ts b/ui/src/app/edge/settings/app/single.component.ts index 0f6ffb92983..bacbbf39ceb 100644 --- a/ui/src/app/edge/settings/app/single.component.ts +++ b/ui/src/app/edge/settings/app/single.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, HostListener, OnDestroy, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { DomSanitizer } from '@angular/platform-browser'; diff --git a/ui/src/app/edge/settings/app/update.component.ts b/ui/src/app/edge/settings/app/update.component.ts index 82c1817b320..f097862c3ba 100644 --- a/ui/src/app/edge/settings/app/update.component.ts +++ b/ui/src/app/edge/settings/app/update.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; diff --git a/ui/src/app/edge/settings/channels/channels.component.ts b/ui/src/app/edge/settings/channels/channels.component.ts index 436173cd76a..2709b475a2e 100644 --- a/ui/src/app/edge/settings/channels/channels.component.ts +++ b/ui/src/app/edge/settings/channels/channels.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; @@ -77,7 +78,7 @@ export class ChannelsComponent { return; } - if (PersistencePriority.isAtLeast(channelConfig.persistencePriority, globalPersistencePriority)) { + if (PersistencePriority.isLessThan(channelConfig.persistencePriority, globalPersistencePriority)) { this.componentChannelConfig.set(address.toString(), { ...this.config.getChannel(address), ...{ showPersistencePriority: true } }); } } diff --git a/ui/src/app/edge/settings/component/install/index.component.ts b/ui/src/app/edge/settings/component/install/index.component.ts index e15b516c35a..147379b3235 100644 --- a/ui/src/app/edge/settings/component/install/index.component.ts +++ b/ui/src/app/edge/settings/component/install/index.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ActivatedRoute } from '@angular/router'; import { CategorizedFactories } from 'src/app/shared/edge/edgeconfig'; import { Component, OnInit } from '@angular/core'; diff --git a/ui/src/app/edge/settings/component/install/install.component.ts b/ui/src/app/edge/settings/component/install/install.component.ts index 40ede1a058a..89ad5585219 100644 --- a/ui/src/app/edge/settings/component/install/install.component.ts +++ b/ui/src/app/edge/settings/component/install/install.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ActivatedRoute } from '@angular/router'; import { Component, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; diff --git a/ui/src/app/edge/settings/component/update/index.component.ts b/ui/src/app/edge/settings/component/update/index.component.ts index c8eb4efa668..fcec4e80b41 100644 --- a/ui/src/app/edge/settings/component/update/index.component.ts +++ b/ui/src/app/edge/settings/component/update/index.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { CategorizedComponents } from 'src/app/shared/edge/edgeconfig'; diff --git a/ui/src/app/edge/settings/component/update/update.component.ts b/ui/src/app/edge/settings/component/update/update.component.ts index 4e7cb09fc02..e4e0f1b7f5b 100644 --- a/ui/src/app/edge/settings/component/update/update.component.ts +++ b/ui/src/app/edge/settings/component/update/update.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ActivatedRoute } from '@angular/router'; import { Component, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; diff --git a/ui/src/app/edge/settings/jsonrpctest/jsonrpctest.html b/ui/src/app/edge/settings/jsonrpctest/jsonrpctest.html new file mode 100644 index 00000000000..d10ae978751 --- /dev/null +++ b/ui/src/app/edge/settings/jsonrpctest/jsonrpctest.html @@ -0,0 +1,191 @@ + + + + + + { +
    +
    + + +   + + "{{item.key}}": + , +
    + + +   + + } + + + string + + + + [] + + +
    + +
    + + + + + + + + + + + + {{parent.method}} ➔ + + {{ endpoint.method }} + + {{tag.name}} + + + + + + + +
    +
    +
    +
    + + + {{guard.name}} {{guard.description}} + + + + +
    + Request +
    + + +
    + +
    + Response +
    + + +
    +
    + + + Examples + + + + + + {{example.key}} + + + +
    +
    +
    {{example.value | json}}
    +
    +
    +
    + + + + + {{example.key}} + + + +
    +
    +
    {{example.value | json}}
    +
    +
    +
    +
    + + + + Try + now! + + + +
    + + Request + + + + + + Raw + + + Form + + + + + + +
    + + + +
    +
    + Form +
    +
    +
    + + + + + Run! + + + + + + Response + + + + +
    {{endpoint.fetch?.response}}
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    diff --git a/ui/src/app/edge/settings/jsonrpctest/jsonrpctest.ts b/ui/src/app/edge/settings/jsonrpctest/jsonrpctest.ts new file mode 100644 index 00000000000..ce87c5b6db7 --- /dev/null +++ b/ui/src/app/edge/settings/jsonrpctest/jsonrpctest.ts @@ -0,0 +1,188 @@ +// @ts-strict-ignore +import { Component, OnInit } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { JsonrpcRequest } from 'src/app/shared/jsonrpc/base'; +import { Edge, Service, Websocket } from 'src/app/shared/shared'; +import { environment } from 'src/environments'; + +@Component({ + selector: JsonrpcTestComponent.SELECTOR, + templateUrl: './jsonrpctest.html', +}) +export class JsonrpcTestComponent implements OnInit { + + private static readonly SELECTOR = "jsonrpcTest"; + + private edge: Edge | undefined; + protected endpoints: Endpoint[] = []; + + constructor( + private route: ActivatedRoute, + private service: Service, + private websocket: Websocket, + ) { + + } + + public ngOnInit(): void { + this.service.setCurrentComponent('Jsonrpc Test', this.route).then(edge => { + this.edge = edge; + edge.sendRequest(this.websocket, new JsonrpcRequest("routes", {})).then(response => { + this.endpoints = (response.result['endpoints'] as EndpointResponse[]).map(endpoint => { + return { + method: endpoint.method, + description: endpoint.description ? endpoint.description.replace('\n', '
    ') : null, + tags: endpoint.tags, + guards: endpoint.guards, + request: endpoint.request, // JSON.stringify(endpoint.request.json, null, 2), + response: endpoint.response, + parent: endpoint.parent, + requestMethod: 'raw', + form: new FormGroup({}), + model: {}, + modelRaw: JSON.stringify(createDummyRequest(endpoint.request.json), null, 2), + }; + }); + }); + }).catch(e => { + this.service.toast(e, 'danger'); + }); + } + + protected request(endpoint: Endpoint): void { + console.log("Run!", endpoint); + if (!endpoint.fetch) { + endpoint.fetch = {}; + } + endpoint.fetch.loading = true; + this.service.startSpinnerTransparentBackground(endpoint.method); + + let request: JsonrpcRequest = new JsonrpcRequest( + endpoint.method, + endpoint.modelRaw ? JSON.parse(endpoint.modelRaw) : {}, + ); + for (let i = endpoint.parent.length - 1; i >= 0; i--) { + const parent = endpoint.parent[i]; + if (environment.backend === 'OpenEMS Backend') { + if (parent.method === 'authenticatedRpc') { + break; + } + } + + const lastRequest = request; + request = new JsonrpcRequest( + parent.method, { + ...parent.request.base, + }, + ); + const lastObj = request.params; + for (let j = 0; j < parent.request.pathToSubrequest.length; j++) { + const path = parent.request.pathToSubrequest[j]; + if (j === parent.request.pathToSubrequest.length - 1) { + lastObj[path] = lastRequest; + } else { + lastObj[path] = {}; + } + } + } + + + (environment.backend === 'OpenEMS Edge' + ? this.websocket.sendRequest(request) + : this.edge.sendRequest(this.websocket, request)) + .then(response => { + endpoint.fetch.response = JSON.stringify(response, null, 2); + }).catch(error => { + endpoint.fetch.response = JSON.stringify(error, null, 2); + }).finally(() => { + endpoint.fetch.loading = false; + this.service.stopSpinner(endpoint.method); + }); + } + +} + +function createDummyRequest(endpointType?: EndpointType) { + if (!endpointType) { + return undefined; + } + switch (endpointType.type) { + case 'object': { + const obj = {}; + for (const [key, value] of Object.entries(endpointType.properties)) { + obj[key] = createDummyRequest(value); + } + return obj; + } + case 'string': { + return 'string'; + } + } +} + +type EndpointResponse = { + method: string, + description: string, + tags: Tag[], + guards: Guard[], + request: { + json: EndpointType, + examples: RequestExample[] + }, + response: { + json: EndpointType, + examples: RequestExample[] + }, + parent: { method: string, request: { base: any, pathToSubrequest: string[] } }[], +} + +type Tag = { + name: string +} + +type Guard = { + name: string, + description: string +} + +type RequestExample = { + key: string, + value: {} +} + +type EndpointType = + { + type: 'object', + properties: { [key: string]: EndpointType } + } + | { + type: 'string', + constraints: string[] + } + +type Endpoint = { + method: string, + description: string, + tags: Tag[], + guards: Guard[], + request: { + json: EndpointType, + examples: RequestExample[], + selectedExample?: string, + }, + response: { + json: EndpointType, + examples: RequestExample[], + }, + parent: { method: string, request: { base: any, pathToSubrequest: string[] } }[], + tryRequest?: boolean, + requestMethod: string, + form: FormGroup; + model: any; + modelRaw: string; + fetch?: { + loading?: boolean, + response?: string; + } +} diff --git a/ui/src/app/edge/settings/jsonrpctest/permission.ts b/ui/src/app/edge/settings/jsonrpctest/permission.ts new file mode 100644 index 00000000000..38b82cf42eb --- /dev/null +++ b/ui/src/app/edge/settings/jsonrpctest/permission.ts @@ -0,0 +1,7 @@ +import { Edge } from "src/app/shared/edge/edge"; +import { User } from "src/app/shared/jsonrpc/shared"; + +export function canSeeJsonrpcTest(user: User, edge: Edge): boolean { + // TODO check for certain users + return true; +} diff --git a/ui/src/app/edge/settings/network/network.component.ts b/ui/src/app/edge/settings/network/network.component.ts index 3be20a409e9..6d56284c258 100644 --- a/ui/src/app/edge/settings/network/network.component.ts +++ b/ui/src/app/edge/settings/network/network.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; diff --git a/ui/src/app/edge/settings/profile/aliasupdate.component.ts b/ui/src/app/edge/settings/profile/aliasupdate.component.ts index d916b716704..43c62955fbe 100644 --- a/ui/src/app/edge/settings/profile/aliasupdate.component.ts +++ b/ui/src/app/edge/settings/profile/aliasupdate.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; diff --git a/ui/src/app/edge/settings/profile/modbusapi/getModbusProtocolResponse.ts b/ui/src/app/edge/settings/profile/modbusapi/getModbusProtocolResponse.ts index d1bbe48e7c7..cc25ca0bbb9 100644 --- a/ui/src/app/edge/settings/profile/modbusapi/getModbusProtocolResponse.ts +++ b/ui/src/app/edge/settings/profile/modbusapi/getModbusProtocolResponse.ts @@ -22,8 +22,8 @@ import { JsonrpcResponseSuccess } from '../../../../shared/jsonrpc/base'; export class GetModbusProtocolResponse extends JsonrpcResponseSuccess { public constructor( - public readonly id: string, - public readonly result: { + public override readonly id: string, + public override readonly result: { table: [{ ref: number, name: string, diff --git a/ui/src/app/edge/settings/profile/profile.component.ts b/ui/src/app/edge/settings/profile/profile.component.ts index baba0377806..73b75bfab1d 100644 --- a/ui/src/app/edge/settings/profile/profile.component.ts +++ b/ui/src/app/edge/settings/profile/profile.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { PopoverController } from '@ionic/angular'; diff --git a/ui/src/app/edge/settings/settings.component.html b/ui/src/app/edge/settings/settings.component.html index 51c5353aaec..dcc60d91df9 100644 --- a/ui/src/app/edge/settings/settings.component.html +++ b/ui/src/app/edge/settings/settings.component.html @@ -88,6 +88,20 @@ + + + + + + Jsonrpc test + + + + + + + + diff --git a/ui/src/app/edge/settings/settings.component.ts b/ui/src/app/edge/settings/settings.component.ts index 5076ca530a8..04bc4542cc9 100644 --- a/ui/src/app/edge/settings/settings.component.ts +++ b/ui/src/app/edge/settings/settings.component.ts @@ -1,8 +1,10 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { environment } from 'src/environments'; import { Edge, Service, Utils } from '../../shared/shared'; import { canSeeAppCenter } from './app/permissions'; +import { canSeeJsonrpcTest } from './jsonrpctest/permission'; @Component({ selector: 'settings', @@ -14,6 +16,8 @@ export class SettingsComponent implements OnInit { public environment = environment; public canSeeAppCenter: boolean | undefined; + public canSeeJsonrpcTest: boolean | undefined; + protected isEdgeBackend: boolean = environment.backend === 'OpenEMS Edge'; constructor( @@ -26,7 +30,9 @@ export class SettingsComponent implements OnInit { public ngOnInit() { this.service.getCurrentEdge().then(edge => { this.edge = edge; + const user = this.service.metadata?.value?.user; this.canSeeAppCenter = canSeeAppCenter(this.edge); + this.canSeeJsonrpcTest = canSeeJsonrpcTest(user, edge); }); } } diff --git a/ui/src/app/edge/settings/settings.module.ts b/ui/src/app/edge/settings/settings.module.ts index 4c5b3920c50..a98f0f24b3d 100644 --- a/ui/src/app/edge/settings/settings.module.ts +++ b/ui/src/app/edge/settings/settings.module.ts @@ -9,6 +9,7 @@ import { IndexComponent as ComponentInstallIndexComponent } from './component/in import { ComponentInstallComponent } from './component/install/install.component'; import { IndexComponent as ComponentUpdateIndexComponent } from './component/update/index.component'; import { ComponentUpdateComponent } from './component/update/update.component'; +import { JsonrpcTestComponent } from './jsonrpctest/jsonrpctest'; import { NetworkComponent } from './network/network.component'; import { AliasUpdateComponent } from './profile/aliasupdate.component'; import { ProfileComponent } from './profile/profile.component'; @@ -32,6 +33,7 @@ import { SystemExecuteComponent } from './systemexecute/systemexecute.component' ComponentInstallIndexComponent, ComponentUpdateComponent, ComponentUpdateIndexComponent, + JsonrpcTestComponent, MaintenanceComponent, NetworkComponent, OeSystemUpdateComponent, @@ -40,8 +42,6 @@ import { SystemExecuteComponent } from './systemexecute/systemexecute.component' SystemComponent, SystemExecuteComponent, ], - entryComponents: [ - ], exports: [ OeSystemUpdateComponent, ], diff --git a/ui/src/app/edge/settings/system/executeSystemUpdate.ts b/ui/src/app/edge/settings/system/executeSystemUpdate.ts index b237ec5149b..00b806ce07c 100644 --- a/ui/src/app/edge/settings/system/executeSystemUpdate.ts +++ b/ui/src/app/edge/settings/system/executeSystemUpdate.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Subject, timer } from "rxjs"; import { takeUntil } from "rxjs/operators"; import { ComponentJsonApiRequest } from "src/app/shared/jsonrpc/request/componentJsonApiRequest"; diff --git a/ui/src/app/edge/settings/system/executesystemupdate.component.ts b/ui/src/app/edge/settings/system/executesystemupdate.component.ts index b2f8c2e35af..8ec0694b34e 100644 --- a/ui/src/app/edge/settings/system/executesystemupdate.component.ts +++ b/ui/src/app/edge/settings/system/executesystemupdate.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Edge, Service, Websocket } from 'src/app/shared/shared'; import { environment } from 'src/environments'; diff --git a/ui/src/app/edge/settings/system/maintenance/maintenance.ts b/ui/src/app/edge/settings/system/maintenance/maintenance.ts index 9f74a0846b0..34e27d47c15 100644 --- a/ui/src/app/edge/settings/system/maintenance/maintenance.ts +++ b/ui/src/app/edge/settings/system/maintenance/maintenance.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { AlertController } from '@ionic/angular'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/edge/settings/system/oe-system-update.component.ts b/ui/src/app/edge/settings/system/oe-system-update.component.ts index d08c16bc6ff..fe7847136fc 100644 --- a/ui/src/app/edge/settings/system/oe-system-update.component.ts +++ b/ui/src/app/edge/settings/system/oe-system-update.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { Edge, Service, Websocket } from 'src/app/shared/shared'; import { environment } from 'src/environments'; diff --git a/ui/src/app/edge/settings/system/system.component.ts b/ui/src/app/edge/settings/system/system.component.ts index c88f08fb295..11c23429957 100644 --- a/ui/src/app/edge/settings/system/system.component.ts +++ b/ui/src/app/edge/settings/system/system.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { environment } from 'src/environments'; diff --git a/ui/src/app/edge/settings/systemexecute/systemexecute.component.ts b/ui/src/app/edge/settings/systemexecute/systemexecute.component.ts index abbf8083096..00db9a6ec92 100644 --- a/ui/src/app/edge/settings/systemexecute/systemexecute.component.ts +++ b/ui/src/app/edge/settings/systemexecute/systemexecute.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; diff --git a/ui/src/app/edge/settings/systemlog/systemlog.component.ts b/ui/src/app/edge/settings/systemlog/systemlog.component.ts index 1eac0d407fc..2785a1df4b7 100644 --- a/ui/src/app/edge/settings/systemlog/systemlog.component.ts +++ b/ui/src/app/edge/settings/systemlog/systemlog.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ActivatedRoute } from '@angular/router'; import { Component, OnDestroy, OnInit } from '@angular/core'; import { parse } from 'date-fns'; diff --git a/ui/src/app/index/filter/filter.component.ts b/ui/src/app/index/filter/filter.component.ts index 9015a779216..a7598c9c8e2 100644 --- a/ui/src/app/index/filter/filter.component.ts +++ b/ui/src/app/index/filter/filter.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, EventEmitter, Output } from "@angular/core"; import { TranslateService } from "@ngx-translate/core"; import { TKeyValue } from "src/app/shared/service/defaulttypes"; diff --git a/ui/src/app/index/login.component.ts b/ui/src/app/index/login.component.ts index b6f489c6562..4cb1c9ff054 100644 --- a/ui/src/app/index/login.component.ts +++ b/ui/src/app/index/login.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { AfterContentChecked, ChangeDetectorRef, Component, OnDestroy, OnInit } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; diff --git a/ui/src/app/index/login.spec.ts b/ui/src/app/index/login.spec.ts index c41303affae..47f64346695 100644 --- a/ui/src/app/index/login.spec.ts +++ b/ui/src/app/index/login.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { LoginComponent } from "./login.component"; describe('Login', () => { diff --git a/ui/src/app/index/overview/overview.component.ts b/ui/src/app/index/overview/overview.component.ts index 5e8139ce097..cdd0eecbf7f 100644 --- a/ui/src/app/index/overview/overview.component.ts +++ b/ui/src/app/index/overview/overview.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnDestroy, OnInit } from "@angular/core"; import { FormGroup } from "@angular/forms"; import { ActivatedRoute, Router } from "@angular/router"; @@ -82,7 +83,6 @@ export class OverViewComponent implements OnInit, OnDestroy { this.loadNextPage().then((edges) => { this.filteredEdges = edges; - this.page++; }); } diff --git a/ui/src/app/registration/modal/modal.component.ts b/ui/src/app/registration/modal/modal.component.ts index 4589f3f6d5a..a52284aa166 100644 --- a/ui/src/app/registration/modal/modal.component.ts +++ b/ui/src/app/registration/modal/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/shared/chartoptions/chartoptions.component.ts b/ui/src/app/shared/chartoptions/chartoptions.component.ts index 713a05edf1f..d7cefe3fd04 100644 --- a/ui/src/app/shared/chartoptions/chartoptions.component.ts +++ b/ui/src/app/shared/chartoptions/chartoptions.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChartOptionsPopoverComponent } from './popover/popover.component'; import { Component, EventEmitter, Input, Output } from '@angular/core'; import { PopoverController } from '@ionic/angular'; diff --git a/ui/src/app/shared/directive/directive.ts b/ui/src/app/shared/directive/directive.ts index 4e7a62bcafb..ae075455c1a 100644 --- a/ui/src/app/shared/directive/directive.ts +++ b/ui/src/app/shared/directive/directive.ts @@ -6,9 +6,6 @@ import { VarDirective } from './ngvar'; imports: [ BrowserModule, ], - entryComponents: [ - VarDirective, - ], declarations: [ VarDirective, ], diff --git a/ui/src/app/shared/edge/currentdata.ts b/ui/src/app/shared/edge/currentdata.ts index fb71949ab7b..82b9ae647d7 100644 --- a/ui/src/app/shared/edge/currentdata.ts +++ b/ui/src/app/shared/edge/currentdata.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { DefaultTypes } from "../service/defaulttypes"; import { Utils } from "../service/utils"; diff --git a/ui/src/app/shared/edge/edge.ts b/ui/src/app/shared/edge/edge.ts index 3d4640e5419..6be4ef0e4da 100644 --- a/ui/src/app/shared/edge/edge.ts +++ b/ui/src/app/shared/edge/edge.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { compareVersions } from 'compare-versions'; import { BehaviorSubject, Subject } from 'rxjs'; import { SumState } from 'src/app/index/shared/sumState'; diff --git a/ui/src/app/shared/edge/edgeconfig.spec.ts b/ui/src/app/shared/edge/edgeconfig.spec.ts index 38233fe962b..9b6a9bb0b99 100644 --- a/ui/src/app/shared/edge/edgeconfig.spec.ts +++ b/ui/src/app/shared/edge/edgeconfig.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { TimeUnit } from "chart.js"; import { SumState } from "src/app/index/shared/sumState"; @@ -5,7 +6,7 @@ import { TextIndentation } from "../genericComponents/modal/modal-line/modal-lin import { OeChartTester, OeFormlyViewTester } from "../genericComponents/shared/testing/tester"; import { Role } from "../type/role"; import { Edge } from "./edge"; -import { EdgeConfig } from "./edgeconfig"; +import { EdgeConfig, PersistencePriority } from "./edgeconfig"; export namespace DummyConfig { @@ -301,3 +302,14 @@ export namespace ChartConfig { }, }); } + +describe('PersistencePriority', () => { + it('#isLessThan', () => { + expect(PersistencePriority.isLessThan(PersistencePriority.LOW, PersistencePriority.HIGH)).toBe(true); + expect(PersistencePriority.isLessThan(PersistencePriority.VERY_HIGH, PersistencePriority.HIGH)).toBe(false); + expect(PersistencePriority.isLessThan(PersistencePriority.HIGH, PersistencePriority.HIGH)).toBe(false); + expect(PersistencePriority.isLessThan(null, PersistencePriority.HIGH)).toBe(false); + expect(PersistencePriority.isLessThan(undefined, PersistencePriority.HIGH)).toBe(false); + expect(PersistencePriority.isLessThan(undefined, null)).toBe(false); + }); +}); diff --git a/ui/src/app/shared/edge/edgeconfig.ts b/ui/src/app/shared/edge/edgeconfig.ts index 67084f8d382..ebeec7e0231 100644 --- a/ui/src/app/shared/edge/edgeconfig.ts +++ b/ui/src/app/shared/edge/edgeconfig.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChannelAddress } from '../type/channeladdress'; import { Widgets } from '../type/widget'; import { Edge } from './edge'; @@ -635,11 +636,18 @@ export namespace PersistencePriority { export const DEFAULT_CHANNEL_PRIORITY: string = PersistencePriority.VERY_LOW; export const DEFAULT_GLOBAL_PRIORITY: string = PersistencePriority.HIGH; - export function isAtLeast(prio1: string, prio2: string) { + /** + * Checks if given prio1 is less than prio2 + * + * @param prio1 the prio that will be compared + * @param prio2 the prio to compare it to + * @returns true if prio1 is less than prio2 + */ + export function isLessThan(prio1: string, prio2: string): boolean { if (typeof prio1 !== 'string' || typeof prio2 !== 'string') { return false; } - return Object.keys(PersistencePriority).indexOf(prio1) <= Object.keys(PersistencePriority).indexOf(prio2); + return Object.keys(PersistencePriority).indexOf(prio1) < Object.keys(PersistencePriority).indexOf(prio2); } } diff --git a/ui/src/app/shared/edge/meter/esscharger/modal.component.ts b/ui/src/app/shared/edge/meter/esscharger/modal.component.ts index 2af44c5b692..594394991e8 100644 --- a/ui/src/app/shared/edge/meter/esscharger/modal.component.ts +++ b/ui/src/app/shared/edge/meter/esscharger/modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from '@angular/core'; import { Converter } from 'src/app/shared/genericComponents/shared/converter'; import { Utils } from 'src/app/shared/shared'; diff --git a/ui/src/app/shared/edge/meter/meter.module.ts b/ui/src/app/shared/edge/meter/meter.module.ts index f4a3e900bbb..5ecef0c6bd7 100644 --- a/ui/src/app/shared/edge/meter/meter.module.ts +++ b/ui/src/app/shared/edge/meter/meter.module.ts @@ -14,8 +14,6 @@ import { EssChargerComponent } from "./esscharger/modal.component"; PipeModule, Generic_ComponentsModule, ], - entryComponents: [ - ], declarations: [ ElectricityMeterComponent, EssChargerComponent, diff --git a/ui/src/app/shared/footer/footer.html b/ui/src/app/shared/footer/footer.html index 44f7cf74119..1ff1460ea74 100644 --- a/ui/src/app/shared/footer/footer.html +++ b/ui/src/app/shared/footer/footer.html @@ -3,7 +3,7 @@ - + {{displayValues.comment}} | diff --git a/ui/src/app/shared/footer/footer.ts b/ui/src/app/shared/footer/footer.ts index 5e9f34f986f..38539365640 100644 --- a/ui/src/app/shared/footer/footer.ts +++ b/ui/src/app/shared/footer/footer.ts @@ -21,11 +21,11 @@ import { Role } from "../type/role"; width: 100%; font-size: 14px !important; - ion-row: { + ion-row { text-align: center; } - ion-item: { + ion-item { --min-height: initial !important; font-size: inherit; } @@ -37,7 +37,7 @@ export class FooterComponent implements OnInit { protected user: User | null = null; protected edge: Edge | null = null; - protected displayValues: { version: string, id: string, comment: string } | null = null; + protected displayValues: { comment: string, id: string, version: string } | null = null; protected isAtLeastOwner: boolean | null = null; @HostBinding('attr.data-isSmartPhone') @@ -57,7 +57,7 @@ export class FooterComponent implements OnInit { let title = environment.edgeShortName; if (edge) { - this.displayValues = FooterComponent.getDisplayValues(edge); + this.displayValues = FooterComponent.getDisplayValues(this.user, edge); if (this.user.hasMultipleEdges) { title += " | " + edge.id; @@ -65,16 +65,30 @@ export class FooterComponent implements OnInit { } this.title.setTitle(title); - this.isAtLeastOwner = Role.isAtLeast(this.user.globalRole, Role.OWNER); }); }); } - private static getDisplayValues(edge: Edge): { version: string; id: string; comment: string; } { - return { - comment: edge?.comment, - id: edge.id, + private static getDisplayValues(user: User, edge: Edge): { comment: string, id: string, version: string } { + const result = { + comment: "", + id: "", version: edge.version, }; + + switch (environment.backend) { + case "OpenEMS Backend": + if (Role.isAtLeast(user.globalRole, Role.OWNER) && user.hasMultipleEdges) { + result.comment = edge?.comment; + } + result.id = edge.id; + break; + + case "OpenEMS Edge": + result.id = environment.edgeShortName; + break; + } + + return result; } } diff --git a/ui/src/app/shared/formly/form-field-checkbox-hyperlink/form-field-checkbox-hyperlink.wrapper.ts b/ui/src/app/shared/formly/form-field-checkbox-hyperlink/form-field-checkbox-hyperlink.wrapper.ts index 0270cd13836..c923b6718f3 100644 --- a/ui/src/app/shared/formly/form-field-checkbox-hyperlink/form-field-checkbox-hyperlink.wrapper.ts +++ b/ui/src/app/shared/formly/form-field-checkbox-hyperlink/form-field-checkbox-hyperlink.wrapper.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core'; import { FieldWrapper } from '@ngx-formly/core'; diff --git a/ui/src/app/shared/formly/form-field-default-cases.wrapper.ts b/ui/src/app/shared/formly/form-field-default-cases.wrapper.ts index e99cb765b1e..0b649c05ac8 100644 --- a/ui/src/app/shared/formly/form-field-default-cases.wrapper.ts +++ b/ui/src/app/shared/formly/form-field-default-cases.wrapper.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { AbstractControl } from '@angular/forms'; import { FieldWrapper } from '@ngx-formly/core'; diff --git a/ui/src/app/shared/formly/formly-field-checkbox-image/formly-field-checkbox-with-image.html b/ui/src/app/shared/formly/formly-field-checkbox-image/formly-field-checkbox-with-image.html index 4cd06e749bb..e48b0e25839 100644 --- a/ui/src/app/shared/formly/formly-field-checkbox-image/formly-field-checkbox-with-image.html +++ b/ui/src/app/shared/formly/formly-field-checkbox-image/formly-field-checkbox-with-image.html @@ -10,12 +10,10 @@ - - - - + + + - - \ No newline at end of file + diff --git a/ui/src/app/shared/formly/formly-select-field-modal.component.ts b/ui/src/app/shared/formly/formly-select-field-modal.component.ts index 97128d2f62b..d2a72af4576 100644 --- a/ui/src/app/shared/formly/formly-select-field-modal.component.ts +++ b/ui/src/app/shared/formly/formly-select-field-modal.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from "@angular/core"; import { ModalController } from "@ionic/angular"; diff --git a/ui/src/app/shared/formly/formly-skeleton-wrapper.ts b/ui/src/app/shared/formly/formly-skeleton-wrapper.ts index b7e953ac752..fdc339aebae 100644 --- a/ui/src/app/shared/formly/formly-skeleton-wrapper.ts +++ b/ui/src/app/shared/formly/formly-skeleton-wrapper.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { FormlyFieldConfig } from '@ngx-formly/core'; diff --git a/ui/src/app/shared/genericComponents/abstracthistorywidget.ts b/ui/src/app/shared/genericComponents/abstracthistorywidget.ts index fef3a8d65a0..6786e3e0417 100644 --- a/ui/src/app/shared/genericComponents/abstracthistorywidget.ts +++ b/ui/src/app/shared/genericComponents/abstracthistorywidget.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Directive, Inject, Input, OnChanges, OnDestroy, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { ModalController } from '@ionic/angular'; diff --git a/ui/src/app/shared/genericComponents/chart/abstractHistoryChartOverview.ts b/ui/src/app/shared/genericComponents/chart/abstractHistoryChartOverview.ts index 420d25601c5..ed4e296eb30 100644 --- a/ui/src/app/shared/genericComponents/chart/abstractHistoryChartOverview.ts +++ b/ui/src/app/shared/genericComponents/chart/abstractHistoryChartOverview.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Directive, OnChanges, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { ModalController } from "@ionic/angular"; diff --git a/ui/src/app/shared/genericComponents/chart/abstracthistorychart.ts b/ui/src/app/shared/genericComponents/chart/abstracthistorychart.ts index 811ccaa256b..c64b512460b 100644 --- a/ui/src/app/shared/genericComponents/chart/abstracthistorychart.ts +++ b/ui/src/app/shared/genericComponents/chart/abstracthistorychart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { DecimalPipe, formatNumber } from '@angular/common'; import { ChangeDetectorRef, Directive, Input, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @@ -544,7 +545,7 @@ export abstract class AbstractHistoryChart implements OnInit { * @returns options */ public static getOptions(chartObject: HistoryUtils.ChartData, chartType: 'line' | 'bar', service: Service, - translate: TranslateService, legendOptions: { label: string, strokeThroughHidingStyle: boolean }[], channelData: { data: { [name: string]: number[] } }, locale: string, config: EdgeConfig, datasets: Chart.ChartDataset[]): Chart.ChartOptions { + translate: TranslateService, legendOptions: { label: string, strokeThroughHidingStyle: boolean }[], channelData: { data: { [name: string]: number[] } }, locale: string, config: EdgeConfig): Chart.ChartOptions { let tooltipsLabel: string | null = null; let options: Chart.ChartOptions = Utils.deepCopy(Utils.deepCopy(DEFAULT_TIME_CHART_OPTIONS)); @@ -552,8 +553,7 @@ export abstract class AbstractHistoryChart implements OnInit { const showYAxisTitle: boolean = chartObject.yAxes.length > 1; chartObject.yAxes.forEach((element) => { - const scaleOptions: { min: number, max: number, stepSize: number } | null = ChartConstants.getScaleOptions(datasets, element); - options = AbstractHistoryChart.getYAxisOptions(options, element, translate, chartType, locale, showYAxisTitle, scaleOptions); + options = AbstractHistoryChart.getYAxisOptions(options, element, translate, chartType, locale, showYAxisTitle); }); options.plugins.tooltip.callbacks.title = (tooltipItems: Chart.TooltipItem[]): string => { @@ -697,7 +697,7 @@ export abstract class AbstractHistoryChart implements OnInit { * @param locale the current locale * @returns the chart options {@link Chart.ChartOptions} */ - public static getYAxisOptions(options: Chart.ChartOptions, element: HistoryUtils.yAxes, translate: TranslateService, chartType: 'line' | 'bar', locale: string, showYAxisTitle?: boolean, data?: { min: number, max: number, stepSize: number }): Chart.ChartOptions { + public static getYAxisOptions(options: Chart.ChartOptions, element: HistoryUtils.yAxes, translate: TranslateService, chartType: 'line' | 'bar', locale: string, showYAxisTitle?: boolean): Chart.ChartOptions { const baseConfig = { title: { @@ -712,14 +712,10 @@ export abstract class AbstractHistoryChart implements OnInit { grid: { display: element.displayGrid ?? true, }, - ...(data?.min != null && { min: data.min }), - ...(data?.max != null && { max: data.max }), - ticks: { color: getComputedStyle(document.documentElement).getPropertyValue('--ion-color-text'), padding: 5, maxTicksLimit: ChartConstants.NUMBER_OF_Y_AXIS_TICKS, - ...(data?.stepSize && { stepSize: data.stepSize }), }, }; @@ -773,7 +769,7 @@ export abstract class AbstractHistoryChart implements OnInit { ticks: { ...baseConfig.ticks, padding: 5, - stepSize: 25, + stepSize: 20, }, }; break; @@ -818,7 +814,7 @@ export abstract class AbstractHistoryChart implements OnInit { */ protected setChartLabel() { const locale = this.service.translate.currentLang; - this.options = AbstractHistoryChart.getOptions(this.chartObject, this.chartType, this.service, this.translate, this.legendOptions, this.channelData, locale, this.config, this.datasets); + this.options = AbstractHistoryChart.getOptions(this.chartObject, this.chartType, this.service, this.translate, this.legendOptions, this.channelData, locale, this.config); this.loading = false; this.stopSpinner(); } diff --git a/ui/src/app/shared/genericComponents/chart/chart.constants.spec.ts b/ui/src/app/shared/genericComponents/chart/chart.constants.spec.ts index 7024335ee16..275330f89a6 100644 --- a/ui/src/app/shared/genericComponents/chart/chart.constants.spec.ts +++ b/ui/src/app/shared/genericComponents/chart/chart.constants.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChartDataset } from "chart.js"; import { History } from "src/app/edge/history/common/energy/chart/channels.spec"; diff --git a/ui/src/app/shared/genericComponents/chart/chart.constants.ts b/ui/src/app/shared/genericComponents/chart/chart.constants.ts index f239cbaf399..ebc7711ee78 100644 --- a/ui/src/app/shared/genericComponents/chart/chart.constants.ts +++ b/ui/src/app/shared/genericComponents/chart/chart.constants.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChartDataset } from "chart.js"; import { ArrayUtils } from "../../utils/array/array.utils"; diff --git a/ui/src/app/shared/genericComponents/chart/chart.ts b/ui/src/app/shared/genericComponents/chart/chart.ts index 0927c4f0409..35436b36467 100644 --- a/ui/src/app/shared/genericComponents/chart/chart.ts +++ b/ui/src/app/shared/genericComponents/chart/chart.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { ModalController, PopoverController } from "@ionic/angular"; diff --git a/ui/src/app/shared/genericComponents/flat/abstract-flat-widget-line.ts b/ui/src/app/shared/genericComponents/flat/abstract-flat-widget-line.ts index e3929bd9860..c390e2f6459 100644 --- a/ui/src/app/shared/genericComponents/flat/abstract-flat-widget-line.ts +++ b/ui/src/app/shared/genericComponents/flat/abstract-flat-widget-line.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Directive, Inject, Input, OnChanges, OnDestroy } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { ModalController } from "@ionic/angular"; diff --git a/ui/src/app/shared/genericComponents/flat/abstract-flat-widget.ts b/ui/src/app/shared/genericComponents/flat/abstract-flat-widget.ts index c4b1adcc878..e57ef8c9c83 100644 --- a/ui/src/app/shared/genericComponents/flat/abstract-flat-widget.ts +++ b/ui/src/app/shared/genericComponents/flat/abstract-flat-widget.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Directive, Inject, Input, OnDestroy, OnInit } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; import { ModalController } from "@ionic/angular"; diff --git a/ui/src/app/shared/genericComponents/flat/flat-widget-line/flat-widget-line.ts b/ui/src/app/shared/genericComponents/flat/flat-widget-line/flat-widget-line.ts index 8a6ae6e99b6..7ce3eab9718 100644 --- a/ui/src/app/shared/genericComponents/flat/flat-widget-line/flat-widget-line.ts +++ b/ui/src/app/shared/genericComponents/flat/flat-widget-line/flat-widget-line.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from "@angular/core"; import { AbstractFlatWidgetLine } from "../abstract-flat-widget-line"; diff --git a/ui/src/app/shared/genericComponents/flat/flat.ts b/ui/src/app/shared/genericComponents/flat/flat.ts index c06ab9b9ee0..c15744a15ed 100644 --- a/ui/src/app/shared/genericComponents/flat/flat.ts +++ b/ui/src/app/shared/genericComponents/flat/flat.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from '@angular/core'; import { Icon } from 'src/app/shared/type/widget'; diff --git a/ui/src/app/shared/genericComponents/genericComponents.ts b/ui/src/app/shared/genericComponents/genericComponents.ts index fa0f62b4656..417ac36b721 100644 --- a/ui/src/app/shared/genericComponents/genericComponents.ts +++ b/ui/src/app/shared/genericComponents/genericComponents.ts @@ -33,22 +33,6 @@ import { NotificationComponent } from './shared/notification/notification'; RouterModule, TranslateModule, ], - entryComponents: [ - PickDateComponent, - FlatWidgetComponent, - FlatWidgetLineComponent, - FlatWidgetHorizontalLineComponent, - FlatWidgetPercentagebarComponent, - FlatWidgetLineItemComponent, - ModalButtonsComponent, - ModalInfoLineComponent, - ModalLineComponent, - ModalHorizontalLineComponent, - ModalComponent, - ModalLineItemComponent, - ModalPhasesComponent, - ModalValueLineComponent, - ], declarations: [ // Flat diff --git a/ui/src/app/shared/genericComponents/modal/abstract-modal-line.ts b/ui/src/app/shared/genericComponents/modal/abstract-modal-line.ts index 11437da9071..5381137990c 100644 --- a/ui/src/app/shared/genericComponents/modal/abstract-modal-line.ts +++ b/ui/src/app/shared/genericComponents/modal/abstract-modal-line.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChangeDetectorRef, Directive, Inject, Input, OnChanges, OnDestroy, OnInit } from "@angular/core"; import { FormBuilder, FormGroup } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; diff --git a/ui/src/app/shared/genericComponents/modal/abstractModal.ts b/ui/src/app/shared/genericComponents/modal/abstractModal.ts index e1540757482..c9daf5850f5 100644 --- a/ui/src/app/shared/genericComponents/modal/abstractModal.ts +++ b/ui/src/app/shared/genericComponents/modal/abstractModal.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChangeDetectorRef, Directive, Inject, Input, OnDestroy, OnInit } from "@angular/core"; import { FormBuilder, FormGroup } from "@angular/forms"; import { ActivatedRoute } from "@angular/router"; diff --git a/ui/src/app/shared/genericComponents/modal/help-button/help-button.ts b/ui/src/app/shared/genericComponents/modal/help-button/help-button.ts index fe1fd90e48c..38d4a5025e6 100644 --- a/ui/src/app/shared/genericComponents/modal/help-button/help-button.ts +++ b/ui/src/app/shared/genericComponents/modal/help-button/help-button.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from "@angular/core"; import { Service } from "src/app/shared/shared"; import { environment } from 'src/environments'; diff --git a/ui/src/app/shared/genericComponents/modal/modal-button/modal-button.ts b/ui/src/app/shared/genericComponents/modal/modal-button/modal-button.ts index a29ea8940b4..f1b50ef6328 100644 --- a/ui/src/app/shared/genericComponents/modal/modal-button/modal-button.ts +++ b/ui/src/app/shared/genericComponents/modal/modal-button/modal-button.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from "@angular/core"; import { Icon } from "src/app/shared/type/widget"; import { AbstractModalLine } from "../abstract-modal-line"; diff --git a/ui/src/app/shared/genericComponents/modal/modal-info-line/modal-info-line.ts b/ui/src/app/shared/genericComponents/modal/modal-info-line/modal-info-line.ts index d33fddc59db..ec2b7ff70d1 100644 --- a/ui/src/app/shared/genericComponents/modal/modal-info-line/modal-info-line.ts +++ b/ui/src/app/shared/genericComponents/modal/modal-info-line/modal-info-line.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from "@angular/core"; import { Icon } from "src/app/shared/type/widget"; diff --git a/ui/src/app/shared/genericComponents/modal/modal-line/modal-line.ts b/ui/src/app/shared/genericComponents/modal/modal-line/modal-line.ts index 4cf7619299c..57e97e2e0af 100644 --- a/ui/src/app/shared/genericComponents/modal/modal-line/modal-line.ts +++ b/ui/src/app/shared/genericComponents/modal/modal-line/modal-line.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from "@angular/core"; import { AbstractModalLine } from "../abstract-modal-line"; import { ButtonLabel } from "../modal-button/modal-button"; diff --git a/ui/src/app/shared/genericComponents/modal/modal-value-line/modal-value-line.ts b/ui/src/app/shared/genericComponents/modal/modal-value-line/modal-value-line.ts index fa507440f63..6857fdf23dd 100644 --- a/ui/src/app/shared/genericComponents/modal/modal-value-line/modal-value-line.ts +++ b/ui/src/app/shared/genericComponents/modal/modal-value-line/modal-value-line.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from "@angular/core"; import { ChannelAddress, CurrentData } from "src/app/shared/shared"; diff --git a/ui/src/app/shared/genericComponents/modal/modal.ts b/ui/src/app/shared/genericComponents/modal/modal.ts index c4f9d5f4ab7..10481fb647a 100644 --- a/ui/src/app/shared/genericComponents/modal/modal.ts +++ b/ui/src/app/shared/genericComponents/modal/modal.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from "@angular/core"; import { FormGroup } from "@angular/forms"; import { ModalController } from "@ionic/angular"; diff --git a/ui/src/app/shared/genericComponents/modal/model-horizontal-line/modal-horizontal-line.ts b/ui/src/app/shared/genericComponents/modal/model-horizontal-line/modal-horizontal-line.ts index 7309a5a7e59..03214110464 100644 --- a/ui/src/app/shared/genericComponents/modal/model-horizontal-line/modal-horizontal-line.ts +++ b/ui/src/app/shared/genericComponents/modal/model-horizontal-line/modal-horizontal-line.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from "@angular/core"; /** diff --git a/ui/src/app/shared/genericComponents/shared/converter.ts b/ui/src/app/shared/genericComponents/shared/converter.ts index d6aa1d8fe99..12458d38a0e 100644 --- a/ui/src/app/shared/genericComponents/shared/converter.ts +++ b/ui/src/app/shared/genericComponents/shared/converter.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { TranslateService } from "@ngx-translate/core"; import { CurrentData, EdgeConfig, Utils } from "../../shared"; diff --git a/ui/src/app/shared/genericComponents/shared/dataservice.ts b/ui/src/app/shared/genericComponents/shared/dataservice.ts index 2cc9b167d2a..7af1ff7bb55 100644 --- a/ui/src/app/shared/genericComponents/shared/dataservice.ts +++ b/ui/src/app/shared/genericComponents/shared/dataservice.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Injectable } from "@angular/core"; import { BehaviorSubject, Subject } from "rxjs"; diff --git a/ui/src/app/shared/genericComponents/shared/filter.ts b/ui/src/app/shared/genericComponents/shared/filter.ts index 4203954819f..b1ab407ec79 100644 --- a/ui/src/app/shared/genericComponents/shared/filter.ts +++ b/ui/src/app/shared/genericComponents/shared/filter.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { GridMode } from "../../shared"; export type Filter = (value: number | string | null) => boolean; diff --git a/ui/src/app/shared/genericComponents/shared/testing/common.ts b/ui/src/app/shared/genericComponents/shared/testing/common.ts index a05d77e00ad..06ecc495617 100644 --- a/ui/src/app/shared/genericComponents/shared/testing/common.ts +++ b/ui/src/app/shared/genericComponents/shared/testing/common.ts @@ -27,9 +27,9 @@ export namespace OeTester { options: { "responsive": true, "maintainAspectRatio": false, "elements": { "point": { "radius": 0, "hitRadius": 0, "hoverRadius": 0 }, "line": { "stepped": false, "fill": true } }, "datasets": { "bar": {}, "line": {} }, "plugins": { "colors": { "enabled": false }, "legend": { "display": true, "position": "bottom", "labels": { "color": '' } }, "tooltip": { "intersect": false, "mode": "index", "callbacks": {} } }, "scales": { "x": { "stacked": true, "offset": false, "type": "time", "ticks": { "source": "auto", "maxTicksLimit": 31 }, "bounds": "ticks", "adapters": { "date": { "locale": { "code": "de", "formatLong": {}, "localize": {}, "match": {}, "options": { "weekStartsOn": 1, "firstWeekContainsDate": 4 } } } }, "time": { "unit": period as TimeUnit, "displayFormats": { "datetime": "yyyy-MM-dd HH:mm:ss", "millisecond": "SSS [ms]", "second": "HH:mm:ss a", "minute": "HH:mm", "hour": "HH:00", "day": "dd", "week": "ll", "month": "MM", "quarter": "[Q]Q - YYYY", "year": "yyyy" } } }, "left": { - ...options["left"].scale, ...(chartType === 'line' ? { stacked: false } : {}), "title": { "text": "kW", "display": false, "padding": 5, "font": { "size": 11 } }, "position": "left", "grid": { "display": true }, + ...options["left"]?.scale, ...(chartType === 'line' ? { stacked: false } : {}), "title": { "text": "kW", "display": false, "padding": 5, "font": { "size": 11 } }, "position": "left", "grid": { "display": true }, "ticks": { - ...options["left"].ticks, + ...options["left"]?.ticks, "color": '', "padding": 5, "maxTicksLimit": ChartConstants.NUMBER_OF_Y_AXIS_TICKS, @@ -44,9 +44,9 @@ export namespace OeTester { "responsive": true, "maintainAspectRatio": false, "elements": { "point": { "radius": 0, "hitRadius": 0, "hoverRadius": 0 }, "line": { "stepped": false, "fill": true } }, "datasets": { "bar": { "barPercentage": 1 }, "line": {} }, "plugins": { "colors": { "enabled": false }, "legend": { "display": true, "position": "bottom", "labels": { "color": '' } }, "tooltip": { "intersect": false, "mode": "x", "callbacks": {} } }, "scales": { "x": { "stacked": true, "offset": true, "type": "time", "ticks": { "source": "auto", "maxTicksLimit": 31 }, "bounds": "ticks", "adapters": { "date": { "locale": { "code": "de", "formatLong": {}, "localize": {}, "match": {}, "options": { "weekStartsOn": 1, "firstWeekContainsDate": 4 } } } }, "time": { "unit": period as TimeUnit, "displayFormats": { "datetime": "yyyy-MM-dd HH:mm:ss", "millisecond": "SSS [ms]", "second": "HH:mm:ss a", "minute": "HH:mm", "hour": "HH:00", "day": "dd", "week": "ll", "month": "MM", "quarter": "[Q]Q - YYYY", "year": "yyyy" } } }, "left": { - ...options["left"].scale, ...(chartType === 'line' ? { stacked: false } : {}), "title": { "text": "kWh", "display": false, "padding": 5, "font": { "size": 11 } }, "position": "left", "grid": { "display": true }, + ...options["left"]?.scale, ...(chartType === 'line' ? { stacked: false } : {}), "title": { "text": "kWh", "display": false, "padding": 5, "font": { "size": 11 } }, "position": "left", "grid": { "display": true }, "ticks": { - ...options["left"].ticks, + ...options["left"]?.ticks, "color": '', "padding": 5, "maxTicksLimit": ChartConstants.NUMBER_OF_Y_AXIS_TICKS, diff --git a/ui/src/app/shared/genericComponents/shared/testing/tester.ts b/ui/src/app/shared/genericComponents/shared/testing/tester.ts index ad4bd140916..96559478cc7 100644 --- a/ui/src/app/shared/genericComponents/shared/testing/tester.ts +++ b/ui/src/app/shared/genericComponents/shared/testing/tester.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import * as Chart from "chart.js"; import { ChartDataset } from "chart.js"; import { QueryHistoricTimeseriesDataResponse } from "src/app/shared/jsonrpc/response/queryHistoricTimeseriesDataResponse"; @@ -305,7 +306,7 @@ export class OeChartTester { return { type: 'option', - options: AbstractHistoryChart.getOptions(chartData, chartType, testContext.service, testContext.translate, legendOptions, channelData.result, locale, config, datasets), + options: AbstractHistoryChart.getOptions(chartData, chartType, testContext.service, testContext.translate, legendOptions, channelData.result, locale, config), }; } diff --git a/ui/src/app/shared/header/header.component.ts b/ui/src/app/shared/header/header.component.ts index efd940d50a0..a1b4d2b76a7 100644 --- a/ui/src/app/shared/header/header.component.ts +++ b/ui/src/app/shared/header/header.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { AfterViewChecked, ChangeDetectorRef, Component, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { MenuController, ModalController } from '@ionic/angular'; diff --git a/ui/src/app/shared/history-data-error.component.ts b/ui/src/app/shared/history-data-error.component.ts index d3874ce0576..74df053ac50 100644 --- a/ui/src/app/shared/history-data-error.component.ts +++ b/ui/src/app/shared/history-data-error.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from "@angular/core"; import { JsonrpcResponseError } from "src/app/shared/jsonrpc/base"; diff --git a/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesDataRequest.ts b/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesDataRequest.ts index 7253f7d2340..77146413230 100644 --- a/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesDataRequest.ts +++ b/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesDataRequest.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChannelAddress } from "../../../shared/type/channeladdress"; import { format } from 'date-fns'; import { JsonrpcRequest } from "../base"; diff --git a/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesEnergyPerPeriodRequest.ts b/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesEnergyPerPeriodRequest.ts index 23c68ecbf3f..c81fd430687 100644 --- a/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesEnergyPerPeriodRequest.ts +++ b/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesEnergyPerPeriodRequest.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChannelAddress } from "../../type/channeladdress"; import { format } from 'date-fns'; import { JsonrpcRequest } from "../base"; diff --git a/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesEnergyRequest.ts b/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesEnergyRequest.ts index ec0ec292f68..cd44298b563 100644 --- a/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesEnergyRequest.ts +++ b/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesEnergyRequest.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChannelAddress } from "../../type/channeladdress"; import { format } from 'date-fns'; import { JsonrpcRequest } from "../base"; diff --git a/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesExportXlxs.ts b/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesExportXlxs.ts index a15f8cd76d4..eb67e868a7a 100644 --- a/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesExportXlxs.ts +++ b/ui/src/app/shared/jsonrpc/request/queryHistoricTimeseriesExportXlxs.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { format } from 'date-fns'; import { JsonrpcRequest } from '../base'; diff --git a/ui/src/app/shared/jsonrpc/request/setEmergencyReserveRequest.ts b/ui/src/app/shared/jsonrpc/request/setEmergencyReserveRequest.ts index d8b3865b865..d360e4e16a8 100644 --- a/ui/src/app/shared/jsonrpc/request/setEmergencyReserveRequest.ts +++ b/ui/src/app/shared/jsonrpc/request/setEmergencyReserveRequest.ts @@ -19,7 +19,7 @@ export class SetEmergencyReserveRequest extends JsonrpcRequest { private static METHOD: string = "setEmergencyReserve"; public constructor( - public readonly params: { + public override readonly params: { value: number }, ) { diff --git a/ui/src/app/shared/jsonrpc/request/subscribeChannelsRequest.ts b/ui/src/app/shared/jsonrpc/request/subscribeChannelsRequest.ts index 441ba33ea3b..34a2897aea4 100644 --- a/ui/src/app/shared/jsonrpc/request/subscribeChannelsRequest.ts +++ b/ui/src/app/shared/jsonrpc/request/subscribeChannelsRequest.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ChannelAddress } from "../../../shared/type/channeladdress"; import { JsonrpcRequest } from "../base"; import { JsonRpcUtils } from "../jsonrpcutils"; diff --git a/ui/src/app/shared/jsonrpc/response/edgeRpcResponse.ts b/ui/src/app/shared/jsonrpc/response/edgeRpcResponse.ts index 6d3729db020..8e80ced8991 100644 --- a/ui/src/app/shared/jsonrpc/response/edgeRpcResponse.ts +++ b/ui/src/app/shared/jsonrpc/response/edgeRpcResponse.ts @@ -16,7 +16,7 @@ import { JsonrpcRequest, JsonrpcResponseSuccess } from "../base"; export class EdgeRpcResponse extends JsonrpcResponseSuccess { public constructor( - public readonly id: string, + public override readonly id: string, public readonly params: { payload: JsonrpcRequest }, diff --git a/ui/src/app/shared/percentagebar/percentagebar.component.ts b/ui/src/app/shared/percentagebar/percentagebar.component.ts index f916bdda31b..dfa6da83147 100644 --- a/ui/src/app/shared/percentagebar/percentagebar.component.ts +++ b/ui/src/app/shared/percentagebar/percentagebar.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input } from '@angular/core'; @Component({ diff --git a/ui/src/app/shared/pickdate/pickdate.component.ts b/ui/src/app/shared/pickdate/pickdate.component.ts index 5445b50b0eb..950e0eedf13 100644 --- a/ui/src/app/shared/pickdate/pickdate.component.ts +++ b/ui/src/app/shared/pickdate/pickdate.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { PopoverController } from '@ionic/angular'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/shared/pickdate/popover/popover.component.ts b/ui/src/app/shared/pickdate/popover/popover.component.ts index 5bbb076d201..ad430f4dd75 100644 --- a/ui/src/app/shared/pickdate/popover/popover.component.ts +++ b/ui/src/app/shared/pickdate/popover/popover.component.ts @@ -1,13 +1,14 @@ +// @ts-strict-ignore import { Component, Input, OnInit } from '@angular/core'; import { PopoverController } from '@ionic/angular'; import { TranslateService } from '@ngx-translate/core'; -import { CalAnimation, IAngularMyDpOptions, IMyDate, IMyDateRangeModel } from 'angular-mydatepicker'; import { endOfMonth, startOfMonth } from 'date-fns'; import { addDays, endOfWeek, endOfYear, getDate, getMonth, getYear, startOfWeek, startOfYear } from 'date-fns/esm'; import { Edge } from '../../edge/edge'; import { DefaultTypes } from '../../service/defaulttypes'; import { EdgePermission, Service, Utils } from '../../shared'; +import { CalAnimation, IAngularMyDpOptions, IMyDate, IMyDateRangeModel } from '@nodro7/angular-mydatepicker'; @Component({ selector: 'pickdatepopover', diff --git a/ui/src/app/shared/pipe/classname/classname.pipe.ts b/ui/src/app/shared/pipe/classname/classname.pipe.ts index 386c9241826..7b966b8add5 100644 --- a/ui/src/app/shared/pipe/classname/classname.pipe.ts +++ b/ui/src/app/shared/pipe/classname/classname.pipe.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ diff --git a/ui/src/app/shared/pipe/keys/keys.pipe.ts b/ui/src/app/shared/pipe/keys/keys.pipe.ts index 7098244b582..3d4eb68073d 100644 --- a/ui/src/app/shared/pipe/keys/keys.pipe.ts +++ b/ui/src/app/shared/pipe/keys/keys.pipe.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ diff --git a/ui/src/app/shared/pipe/pipe.ts b/ui/src/app/shared/pipe/pipe.ts index 85b06b08628..e2fef054ecf 100644 --- a/ui/src/app/shared/pipe/pipe.ts +++ b/ui/src/app/shared/pipe/pipe.ts @@ -14,16 +14,6 @@ import { VersionPipe } from './version/version.pipe'; imports: [ BrowserModule, ], - entryComponents: [ - UnitvaluePipe, - SignPipe, - FormatSecondsToDurationPipe, - KeysPipe, - IsclassPipe, - ClassnamePipe, - VersionPipe, - TypeofPipe, - ], declarations: [ UnitvaluePipe, SignPipe, diff --git a/ui/src/app/shared/pipe/sign/sign.pipe.ts b/ui/src/app/shared/pipe/sign/sign.pipe.ts index dafe66279b7..614ff7c928b 100644 --- a/ui/src/app/shared/pipe/sign/sign.pipe.ts +++ b/ui/src/app/shared/pipe/sign/sign.pipe.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ diff --git a/ui/src/app/shared/service/abstractservice.ts b/ui/src/app/shared/service/abstractservice.ts index 51cb2ffb876..02143b36ac0 100644 --- a/ui/src/app/shared/service/abstractservice.ts +++ b/ui/src/app/shared/service/abstractservice.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ErrorHandler } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { BehaviorSubject } from 'rxjs'; diff --git a/ui/src/app/shared/service/defaulttypes.ts b/ui/src/app/shared/service/defaulttypes.ts index e9762bda4f4..98527a673a6 100644 --- a/ui/src/app/shared/service/defaulttypes.ts +++ b/ui/src/app/shared/service/defaulttypes.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { TranslateService } from '@ngx-translate/core'; import { endOfMonth, endOfYear, format, getDay, getMonth, getYear, isSameDay, isSameMonth, isSameYear, startOfMonth, startOfYear, subDays } from 'date-fns'; diff --git a/ui/src/app/shared/service/globalRouteChangeHandler.ts b/ui/src/app/shared/service/globalRouteChangeHandler.ts index 17495ca8d69..af6db888e34 100644 --- a/ui/src/app/shared/service/globalRouteChangeHandler.ts +++ b/ui/src/app/shared/service/globalRouteChangeHandler.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Injectable } from "@angular/core"; import { Router, RoutesRecognized } from "@angular/router"; import { TranslateService } from "@ngx-translate/core"; diff --git a/ui/src/app/shared/service/pagination.ts b/ui/src/app/shared/service/pagination.ts index 96956983461..61d1f1aea1c 100644 --- a/ui/src/app/shared/service/pagination.ts +++ b/ui/src/app/shared/service/pagination.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Directive } from '@angular/core'; import { Router } from '@angular/router'; import { SubscribeEdgesRequest } from '../jsonrpc/request/subscribeEdgesRequest'; diff --git a/ui/src/app/shared/service/service.ts b/ui/src/app/shared/service/service.ts index 5a724cb6fe8..771b140ab6e 100644 --- a/ui/src/app/shared/service/service.ts +++ b/ui/src/app/shared/service/service.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { registerLocaleData } from '@angular/common'; import { Injectable } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; diff --git a/ui/src/app/shared/service/utils.spec.ts b/ui/src/app/shared/service/utils.spec.ts index 816379415c1..49a2ae3d31a 100644 --- a/ui/src/app/shared/service/utils.spec.ts +++ b/ui/src/app/shared/service/utils.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Utils } from "./utils"; describe('Utils', () => { diff --git a/ui/src/app/shared/service/utils.ts b/ui/src/app/shared/service/utils.ts index 9d1792028c5..d3f9652c6e5 100644 --- a/ui/src/app/shared/service/utils.ts +++ b/ui/src/app/shared/service/utils.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { formatNumber } from '@angular/common'; import { TranslateService } from '@ngx-translate/core'; import { ChartDataset } from 'chart.js'; @@ -773,24 +774,12 @@ export namespace HistoryUtils { export namespace TimeOfUseTariffUtils { - export type ScheduleChartData = { - datasets: ChartDataset[], - colors: any[], - labels: Date[] - } - - export enum TimeOfUseTariffState { + export enum State { DelayDischarge = 0, Balancing = 1, - ChargeProduction = 2, ChargeGrid = 3, } - export enum ControlMode { - CHARGE_CONSUMPTION = 'CHARGE_CONSUMPTION', - DELAY_DISCHARGE = 'DELAY_DISCHARGE' - } - /** * Converts a value in €/MWh to €Ct./kWh. * @@ -808,127 +797,6 @@ export namespace TimeOfUseTariffUtils { } } - /** - * Gets the schedule chart data containing datasets, colors and labels. - * - * @param size The length of the dataset - * @param prices The Time-of-Use-Tariff quarterly price array - * @param states The Time-of-Use-Tariff state array - * @param timestamps The Time-of-Use-Tariff timestamps array - * @param gridBuy The Time-of-Use-Tariff gridBuy array - * @param socArray The Time-of0Use-Tariff soc Array. - * @param translate The Translate service - * @param controlMode The Control mode of the controller. - * @returns The ScheduleChartData. - */ - export function getScheduleChartData(size: number, prices: number[], states: number[], timestamps: string[], gridBuy: number[], socArray: number[], translate: TranslateService, controlMode: ControlMode): ScheduleChartData { - const datasets: ChartDataset[] = []; - const colors: any[] = []; - const labels: Date[] = []; - - // Initializing States. - const barChargeGrid = Array(size).fill(null); - const barBalancing = Array(size).fill(null); - const barDelayDischarge = Array(size).fill(null); - - for (let index = 0; index < size; index++) { - const quarterlyPrice = formatPrice(prices[index]); - const state = states[index]; - labels.push(new Date(timestamps[index])); - - if (state !== null) { - switch (state) { - case TimeOfUseTariffState.DelayDischarge: - barDelayDischarge[index] = quarterlyPrice; - break; - case TimeOfUseTariffState.Balancing: - barBalancing[index] = quarterlyPrice; - break; - case TimeOfUseTariffState.ChargeGrid: - barChargeGrid[index] = quarterlyPrice; - break; - } - } - } - - // Set datasets - datasets.push({ - type: 'bar', - label: translate.instant('Edge.Index.Widgets.TIME_OF_USE_TARIFF.STATE.BALANCING'), - data: barBalancing, - order: 1, - }); - colors.push({ - // Dark Green - backgroundColor: 'rgba(51,102,0,0.8)', - borderColor: 'rgba(51,102,0,1)', - }); - - // Set dataset for ChargeGrid. - if (!barChargeGrid.every(v => v === null) || controlMode == ControlMode.CHARGE_CONSUMPTION) { - datasets.push({ - type: 'bar', - label: translate.instant('Edge.Index.Widgets.TIME_OF_USE_TARIFF.STATE.CHARGE_GRID'), - data: barChargeGrid, - order: 1, - }); - colors.push({ - // Sky blue - backgroundColor: 'rgba(0, 204, 204,0.5)', - borderColor: 'rgba(0, 204, 204,0.7)', - }); - } - - // Set dataset for buy from grid - datasets.push({ - type: 'bar', - label: translate.instant('Edge.Index.Widgets.TIME_OF_USE_TARIFF.STATE.DELAY_DISCHARGE'), - data: barDelayDischarge, - order: 1, - }); - colors.push({ - // Black - backgroundColor: 'rgba(0,0,0,0.8)', - borderColor: 'rgba(0,0,0,0.9)', - }); - - // State of charge data - datasets.push({ - type: 'line', - label: translate.instant('General.soc'), - data: socArray, - hidden: false, - yAxisID: ChartAxis.RIGHT, - borderDash: [10, 10], - order: 0, - }); - colors.push({ - backgroundColor: 'rgba(189, 195, 199,0.2)', - borderColor: 'rgba(189, 195, 199,1)', - }); - - datasets.push({ - type: 'line', - label: translate.instant('General.gridBuy'), - data: gridBuy, - hidden: true, - yAxisID: ChartAxis.RIGHT_2, - order: 2, - }); - colors.push({ - backgroundColor: 'rgba(0,0,0, 0.2)', - borderColor: 'rgba(0,0,0, 1)', - }); - - const scheduleChartData: ScheduleChartData = { - colors: colors, - datasets: datasets, - labels: labels, - }; - - return scheduleChartData; - } - /** * Retrieves a formatted label based on the provided value and label type. * @@ -962,12 +830,10 @@ export namespace TimeOfUseTariffUtils { // Show floating point number for values between 0 and 1 return label + ": " + formatNumber(value, 'de', '1.0-4') + " " + currencyLabel; - case gridBuyLabel: - return label + ": " + formatNumber(value, 'de', '1.0-0') + " kW"; - default: + case gridBuyLabel: // Power values - return label + ": " + formatNumber(value, 'de', '1.0-0') + ' ' + 'W'; + return label + ": " + formatNumber(value, 'de', '1.0-2') + " kW"; } } diff --git a/ui/src/app/shared/service/websocket.ts b/ui/src/app/shared/service/websocket.ts index 65c6e196d99..429879edf00 100644 --- a/ui/src/app/shared/service/websocket.ts +++ b/ui/src/app/shared/service/websocket.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/shared/service/websocketInterface.ts b/ui/src/app/shared/service/websocketInterface.ts index 0d5b2b36261..517799b1eb9 100644 --- a/ui/src/app/shared/service/websocketInterface.ts +++ b/ui/src/app/shared/service/websocketInterface.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { JsonrpcNotification, JsonrpcRequest, JsonrpcResponseSuccess } from '../jsonrpc/base'; import { AuthenticateWithPasswordRequest } from '../jsonrpc/request/authenticateWithPasswordRequest'; import { AuthenticateWithTokenRequest } from '../jsonrpc/request/authenticateWithTokenRequest'; diff --git a/ui/src/app/shared/service/wsdata.ts b/ui/src/app/shared/service/wsdata.ts index a0f8511fa45..ea2a0a2b71d 100644 --- a/ui/src/app/shared/service/wsdata.ts +++ b/ui/src/app/shared/service/wsdata.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { WebSocketSubject } from "rxjs/webSocket"; import { environment } from "src/environments"; import { JsonrpcNotification, JsonrpcRequest, JsonrpcResponse, JsonrpcResponseError, JsonrpcResponseSuccess } from "../jsonrpc/base"; diff --git a/ui/src/app/shared/shared.module.ts b/ui/src/app/shared/shared.module.ts index 0f7b7fdeef8..3ea0d81b6cd 100644 --- a/ui/src/app/shared/shared.module.ts +++ b/ui/src/app/shared/shared.module.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { CommonModule } from '@angular/common'; import { Injector, NgModule } from '@angular/core'; import { FormControl, FormsModule, ReactiveFormsModule, ValidationErrors } from '@angular/forms'; diff --git a/ui/src/app/shared/shared.spec.ts b/ui/src/app/shared/shared.spec.ts index 33611a2cf17..82662b2ea1f 100644 --- a/ui/src/app/shared/shared.spec.ts +++ b/ui/src/app/shared/shared.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { SumState } from "../index/shared/sumState"; import { Edge, EdgePermission } from "./shared"; import { Role } from "./type/role"; diff --git a/ui/src/app/shared/shared.ts b/ui/src/app/shared/shared.ts index 2653ae81df8..90b7b4ca963 100644 --- a/ui/src/app/shared/shared.ts +++ b/ui/src/app/shared/shared.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore export { Edge } from "./edge/edge"; export { EdgeConfig } from "./edge/edgeconfig"; export { Logger } from "./service/logger"; diff --git a/ui/src/app/shared/status/single/status.component.ts b/ui/src/app/shared/status/single/status.component.ts index 4355d6dba50..bcc032a56ae 100644 --- a/ui/src/app/shared/status/single/status.component.ts +++ b/ui/src/app/shared/status/single/status.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnDestroy, OnInit } from '@angular/core'; import { ModalController } from '@ionic/angular'; import { Subject } from 'rxjs'; diff --git a/ui/src/app/shared/translate.extension.ts b/ui/src/app/shared/translate.extension.ts index d740e46c5d5..e0d7675b9af 100644 --- a/ui/src/app/shared/translate.extension.ts +++ b/ui/src/app/shared/translate.extension.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { FormlyExtension, FormlyFieldConfig } from '@ngx-formly/core'; import { TranslateService } from '@ngx-translate/core'; diff --git a/ui/src/app/shared/type/language.spec.ts b/ui/src/app/shared/type/language.spec.ts index e4fdefcec4c..d3b078be851 100644 --- a/ui/src/app/shared/type/language.spec.ts +++ b/ui/src/app/shared/type/language.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Language } from "./language"; describe('Language', () => { diff --git a/ui/src/app/shared/type/widget.ts b/ui/src/app/shared/type/widget.ts index 474955e1efe..44370393bed 100644 --- a/ui/src/app/shared/type/widget.ts +++ b/ui/src/app/shared/type/widget.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Edge } from '../edge/edge'; import { EdgeConfig } from '../edge/edgeconfig'; diff --git a/ui/src/app/shared/utils/array/array.utils.spec.ts b/ui/src/app/shared/utils/array/array.utils.spec.ts index 47984ef938f..aab90e1edaa 100644 --- a/ui/src/app/shared/utils/array/array.utils.spec.ts +++ b/ui/src/app/shared/utils/array/array.utils.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ArrayUtils } from "./array.utils"; describe('Array-Utils', () => { diff --git a/ui/src/app/shared/utils/color/color.utils.spec.ts b/ui/src/app/shared/utils/color/color.utils.spec.ts index 3e2c43ad324..d9ee75dd2a8 100644 --- a/ui/src/app/shared/utils/color/color.utils.spec.ts +++ b/ui/src/app/shared/utils/color/color.utils.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { ColorUtils } from "./color.utils"; describe('Color-Utils', () => { diff --git a/ui/src/app/shared/utils/color/color.utils.ts b/ui/src/app/shared/utils/color/color.utils.ts index c2041facddf..e8408861359 100644 --- a/ui/src/app/shared/utils/color/color.utils.ts +++ b/ui/src/app/shared/utils/color/color.utils.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore export namespace ColorUtils { /** diff --git a/ui/src/app/shared/utils/date/dateutils.spec.ts b/ui/src/app/shared/utils/date/dateutils.spec.ts index 266c73d0c29..91af5e6dfb2 100644 --- a/ui/src/app/shared/utils/date/dateutils.spec.ts +++ b/ui/src/app/shared/utils/date/dateutils.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { DateUtils } from "./dateutils"; describe('DateUtils', () => { diff --git a/ui/src/app/shared/utils/datetime/datetime-utils.ts b/ui/src/app/shared/utils/datetime/datetime-utils.ts index 76af3902b26..aec8bad6a21 100644 --- a/ui/src/app/shared/utils/datetime/datetime-utils.ts +++ b/ui/src/app/shared/utils/datetime/datetime-utils.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { format, startOfMonth, startOfYear } from "date-fns"; import { de } from "date-fns/locale"; import { ChronoUnit } from "src/app/edge/history/shared"; diff --git a/ui/src/app/shared/utils/time/timeutils.spec.ts b/ui/src/app/shared/utils/time/timeutils.spec.ts index 03abf88d4f3..0916acd4b76 100644 --- a/ui/src/app/shared/utils/time/timeutils.spec.ts +++ b/ui/src/app/shared/utils/time/timeutils.spec.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { TimeUtils } from "./timeutils"; describe('TimeUtils', () => { diff --git a/ui/src/app/shared/utils/time/timeutils.ts b/ui/src/app/shared/utils/time/timeutils.ts index d53491fbc34..905f2128402 100644 --- a/ui/src/app/shared/utils/time/timeutils.ts +++ b/ui/src/app/shared/utils/time/timeutils.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { DecimalPipe } from "@angular/common"; import { Language } from "../../type/language"; diff --git a/ui/src/app/user/user.component.ts b/ui/src/app/user/user.component.ts index 35d71138e2e..4be869cd4ea 100644 --- a/ui/src/app/user/user.component.ts +++ b/ui/src/app/user/user.component.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Component, OnInit } from '@angular/core'; import { FormGroup, Validators } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; diff --git a/ui/src/assets/i18n/de.json b/ui/src/assets/i18n/de.json index 0b5f4c8cd9e..e1fd38cc65a 100644 --- a/ui/src/assets/i18n/de.json +++ b/ui/src/assets/i18n/de.json @@ -669,11 +669,12 @@ "TIME_OF_INSTALLATION": "Zeitpunkt der Installation", "WARRANTY_TERMS": "Garantiebedingungen", "ACCEPT": "akzeptieren", - "ALIAS_WITH_LABEL_HOME_DC": "Alias MPPT{{mppt}} PV{{pv}}", - "VALUE_WITH_LABEL_HOME_DC": "Wert MPPT{{ mppt }} PV{{ pv }} {{ symbol }}", - "ORIENTATION_WITH_LABEL_HOME_DC": "Ausrichtung MPPT{{ mppt }} PV{{ pv }}", - "MODULE_TYPE_WITH_LABEL_HOME_DC": "Modultyp MPPT{{ mppt }} PV{{ pv }}", - "NUMBER_OF_MODULES_WITH_LABEL_HOME_DC": "Anzahl PV-Module MPPT{{ mppt }} PV{{ pv }}", + "ALIAS_WITH_LABEL_HOME_DC": "Alias MPPT{{mppt}}", + "VALUE_WITH_LABEL_HOME_DC": "Wert MPPT{{ mppt }} {{ symbol }}", + "ORIENTATION_WITH_LABEL_HOME_DC": "Ausrichtung MPPT{{ mppt }}", + "MODULE_TYPE_WITH_LABEL_HOME_DC": "Modultyp MPPT{{ mppt }}", + "NUMBER_OF_MODULES_WITH_LABEL_HOME_DC": "Anzahl PV-Module MPPT{{ mppt }}", + "BOTH_SELECTED_LABEL": "Beide Anschlüsse (\"PV{{ pv1 }}\" und \"PV{{ pv2 }}\") belegt", "DEVICE_CONNECTION_CHECKED": "Das Stromspeichersystem ist an das Stromnetz angeschlossen", "ENERGY_FLOW_METER": { "LABEL": "Installierter Netzzähler" @@ -757,13 +758,16 @@ "SOUTH": "Sued", "WEST": "West" }, - "INSTALLED_POWER": "Installierte leistung [Wₚ]", + "INSTALLED_POWER": "Installierte Leistung [Wₚ]", + "INSTALLED_POWER_PER_STRING": "Installierte Leistung pro String [Wₚ]", "MARKED_AS": " MPPT {{ mppt }} (beschriftet mit \"PV{{ pv }}\")", + "MARKED_AS_BOTH_STRINGS": " MPPT {{ mppt }} (beschriftet mit \"PV{{ pv1 }}\" & \"PV{{ pv2 }}\")", "MODULE_TYPE_DESCRIPTION": "z. B. Hersteller und Leistung", "MODULE_TYPE_WITH_LABEL": "Modultyp {{ label }}{{ number }}", "MODULE_TYPE": "Modultyp", "NUMBER_OF_MODULES_WITH_LABEL": "Anzahl PV-Module {{ label }}{{ number }}", "NUMBER_OF_MODULES": "Anzahl PV-Module", + "NUMBER_OF_MODULES_PER_STRING": "Anzahl PV-Module pro String", "OPEN_MANUAL": "Anleitung öffnen", "ORIENTATION_WITH_LABEL": "Ausrichtung {{ label }}{{ number }}", "ORIENTATION": "Ausrichtung", @@ -771,7 +775,8 @@ "SHADE_MANAGEMENT_DESCRIPTION": "Nur wenn Optimierer verbaut sind, muss das Schattenmanagement deaktiviert werden", "TITLE_PV": "DC-PV Installation (Wechselrichtereingänge)", "TITLE_DC": "DC-PV-Installation", - "DUPLICATE": " MPPT {{ mppt }} doppelt belegt (beschriftet mit \"PV{{ pv }}\")" + "DUPLICATE": " MPPT {{ mppt }} doppelt belegt (beschriftet mit \"PV{{ pv }}\")", + "BOTH_SELECTED_LABEL": "Sind beide Anschlüsse (\"PV{{ pv1 }}\" und \"PV{{ pv2 }}\") belegt?" }, "PROTOCOL_SERIAL_NUMBERS": { "BATTERY_MODULE": "Batteriemodul ", diff --git a/ui/src/assets/i18n/en.json b/ui/src/assets/i18n/en.json index fa02d57c5e8..96a04d721fd 100644 --- a/ui/src/assets/i18n/en.json +++ b/ui/src/assets/i18n/en.json @@ -671,11 +671,12 @@ "TIME_OF_INSTALLATION": "Time of the installation", "WARRANTY_TERMS": "Warranty terms", "ACCEPT": "acceptation", - "ALIAS_WITH_LABEL_HOME_DC": "Alias MPPT{{mppt}} PV{{pv}}", - "VALUE_WITH_LABEL_HOME_DC": "Value MPPT{{ mppt }} PV{{ pv }} {{ symbol }}", - "ORIENTATION_WITH_LABEL_HOME_DC": "MPPT{{ mppt }} PV{{ pv }} Orientation", - "MODULE_TYPE_WITH_LABEL_HOME_DC": "Type of modules MPPT{{ mppt }} PV{{ pv }}", - "NUMBER_OF_MODULES_WITH_LABEL_HOME_DC": "Numnber of PV modules MPPT{{ mppt }} PV{{ pv }}", + "ALIAS_WITH_LABEL_HOME_DC": "Alias MPPT{{mppt}}", + "VALUE_WITH_LABEL_HOME_DC": "Value MPPT{{ mppt }} {{ symbol }}", + "ORIENTATION_WITH_LABEL_HOME_DC": "MPPT{{ mppt }} Orientation", + "MODULE_TYPE_WITH_LABEL_HOME_DC": "Type of modules MPPT{{ mppt }}", + "NUMBER_OF_MODULES_WITH_LABEL_HOME_DC": "Numnber of PV modules MPPT{{ mppt }}", + "BOTH_SELECTED_LABEL": "Both pv-ports (\"PV{{ pv1 }}\" and \"PV{{ pv2 }}\") connected", "DEVICE_CONNECTION_CHECKED": "The energy storage system is connected to the power grid", "ENERGY_FLOW_METER": { "LABEL": "Installed Grid-Meter" @@ -759,13 +760,16 @@ "SOUTH": "South", "WEST": "West" }, - "INSTALLED_POWER": "Installed power[Wₚ]", + "INSTALLED_POWER": "Installed power [Wₚ]", + "INSTALLED_POWER_PER_STRING": "Installed power per string [Wₚ]", "MARKED_AS": " MPPT {{ mppt }} (marked as \"PV{{ pv }}\")", + "MARKED_AS_BOTH_STRINGS": " MPPT {{ mppt }} (marked as \"PV{{ pv1 }}\" & \"PV{{ pv2 }}\")", "MODULE_TYPE_DESCRIPTION": "e.g. Manufuturer and Power", "MODULE_TYPE_WITH_LABEL": "Type of modules {{ label }}{{ number }}", "MODULE_TYPE": "Type of modules", "NUMBER_OF_MODULES_WITH_LABEL": "Number of PV module {{ label }}{{ number }}", - "NUMBER_OF_MODULES": "Number of PV module", + "NUMBER_OF_MODULES": "Number of PV module per string", + "NUMBER_OF_MODULES_PER_STRING": "Number of PV module", "OPEN_MANUAL": "Open manual", "ORIENTATION_WITH_LABEL": "{{ label }}{{ number }} Orientation", "ORIENTATION": "Orientation", @@ -773,7 +777,8 @@ "SHADE_MANAGEMENT_DESCRIPTION": "The shade management has to be disabled, only if optimizers are installed.", "TITLE_PV": "DC-PV installation (Inverter inputs)", "TITLE_DC": "DC-PV-Installation", - "DUPLICATE": " MPPT {{ mppt }} double occupied (marked as \"PV{{ pv }}\")" + "DUPLICATE": " MPPT {{ mppt }} double occupied (marked as \"PV{{ pv }}\")", + "BOTH_SELECTED_LABEL": "Are both ports (\"PV{{ pv1 }}\" & \"PV{{ pv2 }}\") connected?" }, "PROTOCOL_SERIAL_NUMBERS": { "BATTERY_MODULE": "Battery module ", diff --git a/ui/src/assets/img/home-mppt/Home_20_MPPT1.png b/ui/src/assets/img/home-mppt/Home_20_MPPT1.png new file mode 100644 index 00000000000..77bb0695527 Binary files /dev/null and b/ui/src/assets/img/home-mppt/Home_20_MPPT1.png differ diff --git a/ui/src/assets/img/home-mppt/Home_20_MPPT2.png b/ui/src/assets/img/home-mppt/Home_20_MPPT2.png new file mode 100644 index 00000000000..401db52ee89 Binary files /dev/null and b/ui/src/assets/img/home-mppt/Home_20_MPPT2.png differ diff --git a/ui/src/assets/img/home-mppt/Home_30_MPPT1.png b/ui/src/assets/img/home-mppt/Home_30_MPPT1.png new file mode 100644 index 00000000000..2efb461103c Binary files /dev/null and b/ui/src/assets/img/home-mppt/Home_30_MPPT1.png differ diff --git a/ui/src/assets/img/home-mppt/Home_30_MPPT2.png b/ui/src/assets/img/home-mppt/Home_30_MPPT2.png new file mode 100644 index 00000000000..aceeb5735bf Binary files /dev/null and b/ui/src/assets/img/home-mppt/Home_30_MPPT2.png differ diff --git a/ui/src/assets/img/home-mppt/Home_30_MPPT3.png b/ui/src/assets/img/home-mppt/Home_30_MPPT3.png new file mode 100644 index 00000000000..13c35a82192 Binary files /dev/null and b/ui/src/assets/img/home-mppt/Home_30_MPPT3.png differ diff --git a/ui/src/index.html b/ui/src/index.html index c1f60e418a5..747757bf338 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -10,7 +10,7 @@ - + @@ -30,4 +30,4 @@ - \ No newline at end of file + diff --git a/ui/src/themes/openems/environments/backend-dev.ts b/ui/src/themes/openems/environments/backend-dev.ts index f48b00c5e42..0f93f985f5d 100644 --- a/ui/src/themes/openems/environments/backend-dev.ts +++ b/ui/src/themes/openems/environments/backend-dev.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Environment } from "src/environments"; import { theme } from "./theme"; diff --git a/ui/src/themes/openems/environments/backend-docker.ts b/ui/src/themes/openems/environments/backend-docker.ts index 386f296c86d..5ca08fb74f8 100644 --- a/ui/src/themes/openems/environments/backend-docker.ts +++ b/ui/src/themes/openems/environments/backend-docker.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Environment } from "src/environments"; import { theme } from "./theme"; diff --git a/ui/src/themes/openems/environments/backend-prod.ts b/ui/src/themes/openems/environments/backend-prod.ts index ae5bb5eb32f..114c43274f1 100644 --- a/ui/src/themes/openems/environments/backend-prod.ts +++ b/ui/src/themes/openems/environments/backend-prod.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Environment } from "src/environments"; import { theme } from "./theme"; diff --git a/ui/src/themes/openems/environments/edge-dev.ts b/ui/src/themes/openems/environments/edge-dev.ts index 164dd04f34e..a6fd796b39e 100644 --- a/ui/src/themes/openems/environments/edge-dev.ts +++ b/ui/src/themes/openems/environments/edge-dev.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Environment } from "src/environments"; import { theme } from "./theme"; diff --git a/ui/src/themes/openems/environments/edge-docker.ts b/ui/src/themes/openems/environments/edge-docker.ts index d174226e837..80ed1d80a99 100644 --- a/ui/src/themes/openems/environments/edge-docker.ts +++ b/ui/src/themes/openems/environments/edge-docker.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Environment } from "src/environments"; import { theme } from "./theme"; diff --git a/ui/src/themes/openems/environments/edge-prod.ts b/ui/src/themes/openems/environments/edge-prod.ts index bcaf40f6901..4f023294025 100644 --- a/ui/src/themes/openems/environments/edge-prod.ts +++ b/ui/src/themes/openems/environments/edge-prod.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Environment } from "src/environments"; import { theme } from "./theme"; diff --git a/ui/src/themes/openems/environments/gitpod.ts b/ui/src/themes/openems/environments/gitpod.ts index 1ef2f8ca499..4b8f7f22ead 100644 --- a/ui/src/themes/openems/environments/gitpod.ts +++ b/ui/src/themes/openems/environments/gitpod.ts @@ -1,3 +1,4 @@ +// @ts-strict-ignore import { Environment } from "src/environments"; import { theme } from "./theme"; diff --git a/ui/tsconfig.json b/ui/tsconfig.json index 79f74f6da18..997c7208b30 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -9,7 +9,13 @@ "downlevelIteration": true, "experimentalDecorators": true, "moduleResolution": "node", + "plugins": [ + { + "name": "typescript-strict-plugin" + } + ], "importHelpers": true, + "strict": false, "target": "ES2022", "module": "es2020", "noImplicitOverride": true, @@ -27,4 +33,4 @@ "strictInputAccessModifiers": true, "strictTemplates": false } -} \ No newline at end of file +}