From be7179116e13ea70e97ac67cbe16f626767f9888 Mon Sep 17 00:00:00 2001 From: Pierre-Charles David Date: Tue, 19 May 2026 17:21:27 +0200 Subject: [PATCH] [2097] Handle expressions in SysON Bug: https://github.com/eclipse-syson/syson/issues/2097 Signed-off-by: Pierre-Charles David --- CHANGELOG.adoc | 2 + .../SysMLv2PropertiesConfigurer.java | 13 +- .../dto/DeleteExpressionInput.java | 25 + .../ExpressionTextualRepresentationInput.java | 25 + ...xpressionTextualRepresentationPayload.java | 29 + ...ssionTextualRepresentationDataFetcher.java | 56 ++ .../MutationDeleteExpressionDataFetcher.java | 57 ++ .../DeleteExpressionEventHandler.java | 86 +++ ...sionTextualRepresentationEventHandler.java | 68 ++ .../services/DetailsViewService.java | 69 +- .../resources/schema/expressions.graphqls | 15 + .../services/DetailsViewServiceTest.java | 3 +- .../explorer/view/SysONExplorerTests.java | 24 +- ...xpressionsControllersIntegrationTests.java | 649 ++++++++++++++++++ .../CreateExpressionMutationRunner.java | 62 ++ .../DeleteExpressionMutationRunner.java | 61 ++ .../graphql/EditExpressionMutationRunner.java | 62 ++ ...ssionTextualRepresentationQueryRunner.java | 50 ++ .../data/ExpressionSamplesProjectData.java | 37 + .../database-content/ExpressionSamples.sql | 2 +- .../eclipse/syson/sysml/ASTTransformer.java | 105 ++- .../MutationCreateExpressionDataFetcher.java | 56 ++ .../MutationEditExpressionDataFetcher.java | 56 ++ .../sysml/dto/CreateExpressionInput.java | 25 + .../dto/CreateExpressionSuccessPayload.java | 33 + .../syson/sysml/dto/EditExpressionInput.java | 25 + .../dto/EditExpressionSuccessPayload.java | 33 + .../CreateExpressionEventHandler.java | 113 +++ .../services/EditExpressionEventHandler.java | 111 +++ .../InsertTextualSysMLv2EventHandler.java | 72 +- .../services/SysideExpressionEditor.java | 201 ++++++ .../services/SysideSysMLTextImporter.java | 72 ++ .../api/ExpressionCreationResult.java | 27 + .../services/api/ISysMLExpressionEditor.java | 29 + .../services/api/ISysMLTextImporter.java | 28 + .../resources/schema/syson-import.graphqls | 36 +- .../impl/ResultExpressionMembershipImpl.java | 13 +- .../MetamodelQueryElementService.java | 129 ++++ .../SysONDefaultExplorerServices.java | 28 +- ...lorerTreeItemContextMenuEntryProvider.java | 76 +- .../NewObjectAsTextDocumentReport.tsx | 2 +- .../SysONExtensionRegistryMergeStrategy.ts | 2 +- .../DeleteSysMLExpressionMenuContribution.tsx | 59 ++ .../EditSysMLExpressionMenuContribution.tsx | 68 ++ .../expressions/EditSysMLExpressionModal.tsx | 319 +++++++++ .../EditSysMLExpressionModal.types.ts | 31 + .../NewSysMLExpressionMenuContribution.tsx | 68 ++ .../expressions/useCreateExpression.ts | 93 +++ .../expressions/useCreateExpression.types.ts | 46 ++ .../expressions/useDeleteExpression.ts | 71 ++ .../expressions/useDeleteExpression.types.ts | 42 ++ .../expressions/useEditExpression.ts | 95 +++ .../expressions/useEditExpression.types.ts | 47 ++ .../useExpressionTextualRepresentation.ts | 56 ++ ...seExpressionTextualRepresentation.types.ts | 34 + .../registry/SysONExtensionRegistry.ts | 22 +- scripts/check-coverage.jsh | 10 +- 57 files changed, 3544 insertions(+), 184 deletions(-) create mode 100644 backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/DeleteExpressionInput.java create mode 100644 backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/ExpressionTextualRepresentationInput.java create mode 100644 backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/ExpressionTextualRepresentationPayload.java create mode 100644 backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/graphql/EditingContextExpressionTextualRepresentationDataFetcher.java create mode 100644 backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/graphql/MutationDeleteExpressionDataFetcher.java create mode 100644 backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/services/DeleteExpressionEventHandler.java create mode 100644 backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/services/ExpressionTextualRepresentationEventHandler.java create mode 100644 backend/application/syson-application-configuration/src/main/resources/schema/expressions.graphqls create mode 100644 backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/ExpressionsControllersIntegrationTests.java create mode 100644 backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/CreateExpressionMutationRunner.java create mode 100644 backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/DeleteExpressionMutationRunner.java create mode 100644 backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/EditExpressionMutationRunner.java create mode 100644 backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/ExpressionTextualRepresentationQueryRunner.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/datafetchers/MutationCreateExpressionDataFetcher.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/datafetchers/MutationEditExpressionDataFetcher.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/CreateExpressionInput.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/CreateExpressionSuccessPayload.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/EditExpressionInput.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/EditExpressionSuccessPayload.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/CreateExpressionEventHandler.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/EditExpressionEventHandler.java rename backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/{dto => services}/InsertTextualSysMLv2EventHandler.java (60%) create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/SysideExpressionEditor.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/SysideSysMLTextImporter.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ExpressionCreationResult.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ISysMLExpressionEditor.java create mode 100644 backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ISysMLTextImporter.java create mode 100644 frontend/syson-components/src/extensions/expressions/DeleteSysMLExpressionMenuContribution.tsx create mode 100644 frontend/syson-components/src/extensions/expressions/EditSysMLExpressionMenuContribution.tsx create mode 100644 frontend/syson-components/src/extensions/expressions/EditSysMLExpressionModal.tsx create mode 100644 frontend/syson-components/src/extensions/expressions/EditSysMLExpressionModal.types.ts create mode 100644 frontend/syson-components/src/extensions/expressions/NewSysMLExpressionMenuContribution.tsx create mode 100644 frontend/syson-components/src/extensions/expressions/useCreateExpression.ts create mode 100644 frontend/syson-components/src/extensions/expressions/useCreateExpression.types.ts create mode 100644 frontend/syson-components/src/extensions/expressions/useDeleteExpression.ts create mode 100644 frontend/syson-components/src/extensions/expressions/useDeleteExpression.types.ts create mode 100644 frontend/syson-components/src/extensions/expressions/useEditExpression.ts create mode 100644 frontend/syson-components/src/extensions/expressions/useEditExpression.types.ts create mode 100644 frontend/syson-components/src/extensions/expressions/useExpressionTextualRepresentation.ts create mode 100644 frontend/syson-components/src/extensions/expressions/useExpressionTextualRepresentation.types.ts diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 63db67bd6..d8cc6554f 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -24,6 +24,8 @@ - https://github.com/eclipse-syson/syson/issues/2116[#2116] [explorer] In the _Explorer_ view, the items corresponding to the internals of `Expression` elements (syntax tree) are now hidden by default. Disabling the _Hide expression internals_ filter in the _Explorer_ view allows to display them if needed. - https://github.com/eclipse-syson/syson/issues/2112[#2112] [diagrams] Add tools to create _Start_ and _Done_ `StateUsages`, available on `StateUsage` and `StateDefinition` graphical nodes. +- https://github.com/eclipse-syson/syson/issues/2097[#2097] [explorer] Add support for creating and editing exressions through their textual representation. +This is currently supported on `Features` (e.g. `Attribute`), `Constraints` and `Transitions` (guard conditions) view new context menu actions (_Create expression_ and _Edit expression_) on the corresponding elements in the _Explorer_. == v2026.5.0 diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2PropertiesConfigurer.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2PropertiesConfigurer.java index 4384dcac1..ddbf1b316 100644 --- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2PropertiesConfigurer.java +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/configuration/SysMLv2PropertiesConfigurer.java @@ -25,7 +25,6 @@ import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; import org.eclipse.emf.ecore.xmi.impl.XMIResourceImpl; -import org.eclipse.syson.sysml.Element; import org.eclipse.emf.edit.provider.ComposedAdapterFactory.Descriptor; import org.eclipse.sirius.components.collaborative.forms.services.api.IPropertiesDescriptionRegistry; import org.eclipse.sirius.components.collaborative.forms.services.api.IPropertiesDescriptionRegistryConfigurer; @@ -63,8 +62,9 @@ import org.eclipse.syson.model.services.ModelMutationElementService; import org.eclipse.syson.model.services.aql.ModelMutationAQLService; import org.eclipse.syson.model.services.aql.ModelQueryAQLService; -import org.eclipse.syson.services.UtilService; +import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; import org.eclipse.syson.util.AQLConstants; import org.eclipse.syson.util.AQLUtils; import org.eclipse.syson.util.ServiceMethod; @@ -119,10 +119,10 @@ public class SysMLv2PropertiesConfigurer implements IPropertiesDescriptionRegist private final ILabelService labelService; - private final UtilService utilService; - private final IReadOnlyObjectPredicate readOnlyObjectPredicate; + private final MetamodelQueryElementService metamodelQueryElementService; + private final List detailViewHelpTextProviders; public SysMLv2PropertiesConfigurer(List composedAdapterFactoryDescriptors, ViewFormDescriptionConverter converter, IFeedbackMessageService feedbackMessageService, @@ -133,7 +133,7 @@ public SysMLv2PropertiesConfigurer(List composedAdapterFactoryDescri this.labelService = Objects.requireNonNull(labelService); this.readOnlyObjectPredicate = Objects.requireNonNull(readOnlyObjectPredicate); this.detailViewHelpTextProviders = Objects.requireNonNull(detailViewHelpTextProviders); - this.utilService = new UtilService(); + this.metamodelQueryElementService = new MetamodelQueryElementService(); } @Override @@ -155,7 +155,8 @@ public void addPropertiesDescriptions(IPropertiesDescriptionRegistry registry) { // Convert the View-based FormDescription and register the result into the system AQLInterpreter interpreter = new AQLInterpreter(List.of(), - List.of(new DetailsViewService(this.composedAdapterFactoryDescriptors, this.feedbackMessageService, this.readOnlyObjectPredicate, this.detailViewHelpTextProviders), this.labelService, this.utilService, + List.of(new DetailsViewService(this.composedAdapterFactoryDescriptors, this.feedbackMessageService, this.readOnlyObjectPredicate, this.metamodelQueryElementService, + this.detailViewHelpTextProviders), this.labelService, new ModelMutationAQLService(new ModelMutationElementService()), new ModelQueryAQLService(), new FormMutationAQLService(), new FormQueryAQLService()), List.of(SysmlPackage.eINSTANCE)); ViewConverterResult converterResult = this.converter.convert(viewFormDescription, List.of(), interpreter); diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/DeleteExpressionInput.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/DeleteExpressionInput.java new file mode 100644 index 000000000..bda589ea3 --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/DeleteExpressionInput.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.expressions.dto; + +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IInput; + +/** + * The input object for the {@code deleteExpression} mutation. + * + * @author pcdavid + */ +public record DeleteExpressionInput(UUID id, String editingContextId, String parentElementId) implements IInput { +} diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/ExpressionTextualRepresentationInput.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/ExpressionTextualRepresentationInput.java new file mode 100644 index 000000000..24db44567 --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/ExpressionTextualRepresentationInput.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.expressions.dto; + +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IInput; + +/** + * Input for the {@code expressionTextualRepresentation} query field on EditingContext. + * + * @author pcdavid + */ +public record ExpressionTextualRepresentationInput(UUID id, String editingContextId, String elementId) implements IInput { +} diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/ExpressionTextualRepresentationPayload.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/ExpressionTextualRepresentationPayload.java new file mode 100644 index 000000000..3afd3f2b7 --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/dto/ExpressionTextualRepresentationPayload.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.expressions.dto; + +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IPayload; + +/** + * Payload for the {@code expressionTextualRepresentation} query field on EditingContext. + * + * @author pcdavid + */ +public record ExpressionTextualRepresentationPayload(UUID id, String textualRepresentation) implements IPayload { + public ExpressionTextualRepresentationPayload { + Objects.requireNonNull(id); + } +} diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/graphql/EditingContextExpressionTextualRepresentationDataFetcher.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/graphql/EditingContextExpressionTextualRepresentationDataFetcher.java new file mode 100644 index 000000000..e1a53069a --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/graphql/EditingContextExpressionTextualRepresentationDataFetcher.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.expressions.graphql; + +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.IEditingContextDispatcher; +import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationInput; +import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationPayload; + +import graphql.schema.DataFetchingEnvironment; + +/** + * Data fetcher for the field {@code EditingContext#expressionTextualRepresentation} to fetch the textual representation + * of a SysMLv2 expression. + * + * @author pcdavid + */ +@QueryDataFetcher(type = "EditingContext", field = "expressionTextualRepresentation") +public class EditingContextExpressionTextualRepresentationDataFetcher implements IDataFetcherWithFieldCoordinates> { + + private static final String ELEMENT_ID_ARGUMENT = "elementId"; + + private final IEditingContextDispatcher editingContextDispatcher; + + public EditingContextExpressionTextualRepresentationDataFetcher(IEditingContextDispatcher editingContextDispatcher) { + this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher); + } + + @Override + public CompletableFuture get(DataFetchingEnvironment environment) throws Exception { + String editingContextId = environment.getSource(); + String elementId = environment.getArgument(ELEMENT_ID_ARGUMENT); + + ExpressionTextualRepresentationInput input = new ExpressionTextualRepresentationInput(UUID.randomUUID(), editingContextId, elementId); + return this.editingContextDispatcher.dispatchQuery(input.editingContextId(), input) + .filter(ExpressionTextualRepresentationPayload.class::isInstance) + .map(ExpressionTextualRepresentationPayload.class::cast) + .map(ExpressionTextualRepresentationPayload::textualRepresentation) + .toFuture(); + } +} diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/graphql/MutationDeleteExpressionDataFetcher.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/graphql/MutationDeleteExpressionDataFetcher.java new file mode 100644 index 000000000..fd3d60a8a --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/graphql/MutationDeleteExpressionDataFetcher.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.expressions.graphql; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.sirius.components.annotations.spring.graphql.MutationDataFetcher; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.IEditingContextDispatcher; +import org.eclipse.sirius.components.graphql.api.IExceptionWrapper; +import org.eclipse.syson.application.expressions.dto.DeleteExpressionInput; + +import graphql.schema.DataFetchingEnvironment; +import tools.jackson.databind.ObjectMapper; + +/** + * The GraphQL data-fetcher for the {@code deleteExpression} mutation to delete the expression associated to a SysMLv2 + * element. + * + * @author pcdavid + */ +@MutationDataFetcher(type = "Mutation", field = "deleteExpression") +public class MutationDeleteExpressionDataFetcher implements IDataFetcherWithFieldCoordinates> { + + private static final String INPUT_ARGUMENT = "input"; + + private final ObjectMapper objectMapper; + + private final IExceptionWrapper exceptionWrapper; + + private final IEditingContextDispatcher editingContextDispatcher; + + public MutationDeleteExpressionDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEditingContextDispatcher editingContextDispatcher) { + this.objectMapper = Objects.requireNonNull(objectMapper); + this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper); + this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher); + } + + @Override + public CompletableFuture get(DataFetchingEnvironment environment) throws Exception { + Object argument = environment.getArgument(INPUT_ARGUMENT); + var input = this.objectMapper.convertValue(argument, DeleteExpressionInput.class); + return this.exceptionWrapper.wrapMono(() -> this.editingContextDispatcher.dispatchMutation(input.editingContextId(), input), input).toFuture(); + } +} diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/services/DeleteExpressionEventHandler.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/services/DeleteExpressionEventHandler.java new file mode 100644 index 000000000..e8bb9c4b0 --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/services/DeleteExpressionEventHandler.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.expressions.services; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.api.ChangeDescription; +import org.eclipse.sirius.components.collaborative.api.ChangeKind; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventHandler; +import org.eclipse.sirius.components.collaborative.messages.ICollaborativeMessageService; +import org.eclipse.sirius.components.core.api.ErrorPayload; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IInput; +import org.eclipse.sirius.components.core.api.IObjectSearchService; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.core.api.SuccessPayload; +import org.eclipse.syson.application.expressions.dto.DeleteExpressionInput; +import org.eclipse.syson.services.DeleteService; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Expression; +import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; +import org.springframework.stereotype.Service; + +import reactor.core.publisher.Sinks; + +/** + * Event handler for the {@code deleteExpression} mutation. + * + * @author pcdavid + */ +@Service +public class DeleteExpressionEventHandler implements IEditingContextEventHandler { + + private final IObjectSearchService objectSearchService; + + private final MetamodelQueryElementService metamodelQueryElementService; + + private final ICollaborativeMessageService messageService; + + private final DeleteService deleteService; + + public DeleteExpressionEventHandler(IObjectSearchService objectSearchService, ICollaborativeMessageService messageService) { + this.objectSearchService = Objects.requireNonNull(objectSearchService); + this.metamodelQueryElementService = new MetamodelQueryElementService(); + this.messageService = Objects.requireNonNull(messageService); + this.deleteService = new DeleteService(); + } + + @Override + public boolean canHandle(IEditingContext editingContext, IInput input) { + return input instanceof DeleteExpressionInput; + } + + @Override + public void handle(Sinks.One payloadSink, Sinks.Many changeDescriptionSink, IEditingContext editingContext, IInput input) { + IPayload payload; + if (input instanceof DeleteExpressionInput deleteExpressionInput) { + Optional optionalExpression = this.objectSearchService.getObject(editingContext, deleteExpressionInput.parentElementId()) + .filter(Element.class::isInstance) + .map(Element.class::cast) + .flatMap(element -> this.metamodelQueryElementService.findSingleExpressionDefinition(element)); + if (optionalExpression.isPresent()) { + this.deleteService.deleteFromModel(optionalExpression.get()); + var changeDescription = new ChangeDescription(ChangeKind.SEMANTIC_CHANGE, editingContext.getId(), input); + changeDescriptionSink.tryEmitNext(changeDescription); + payload = new SuccessPayload(input.id()); + } else { + payload = new ErrorPayload(input.id(), this.messageService.notFound()); + } + } else { + payload = new ErrorPayload(input.id(), this.messageService.invalidInput(DeleteExpressionInput.class.getName(), input.getClass().getName())); + } + payloadSink.tryEmitValue(payload); + } +} diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/services/ExpressionTextualRepresentationEventHandler.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/services/ExpressionTextualRepresentationEventHandler.java new file mode 100644 index 000000000..63ece7f61 --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/expressions/services/ExpressionTextualRepresentationEventHandler.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.expressions.services; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.api.ChangeDescription; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventHandler; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IInput; +import org.eclipse.sirius.components.core.api.IObjectSearchService; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationInput; +import org.eclipse.syson.application.expressions.dto.ExpressionTextualRepresentationPayload; +import org.eclipse.syson.sysml.Expression; +import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; +import org.springframework.stereotype.Service; + +import reactor.core.publisher.Sinks; + +/** + * Event handler for the {@code expressionTextualRepresentation} query field on EditingContext. + * + * @author pcdavid + */ +@Service +public class ExpressionTextualRepresentationEventHandler implements IEditingContextEventHandler { + + private final IObjectSearchService objectSearchService; + + private final MetamodelQueryElementService metamodelQueryElementService; + + public ExpressionTextualRepresentationEventHandler(IObjectSearchService objectSearchService) { + this.objectSearchService = Objects.requireNonNull(objectSearchService); + this.metamodelQueryElementService = new MetamodelQueryElementService(); + } + + @Override + public boolean canHandle(IEditingContext editingContext, IInput input) { + return input instanceof ExpressionTextualRepresentationInput; + } + + @Override + public void handle(Sinks.One payloadSink, Sinks.Many changeDescriptionSink, IEditingContext editingContext, IInput input) { + String textualRepresentation = ""; + if (input instanceof ExpressionTextualRepresentationInput expressionTextualRepresentationInput) { + Optional optionalExpression = this.objectSearchService.getObject(editingContext, expressionTextualRepresentationInput.elementId()) + .filter(Expression.class::isInstance) + .map(Expression.class::cast) + .filter(this.metamodelQueryElementService::isTopLevelExpression); + if (optionalExpression.isPresent()) { + textualRepresentation = this.metamodelQueryElementService.getExpressionTextualRepresentation(optionalExpression.get()); + } + } + payloadSink.tryEmitValue(new ExpressionTextualRepresentationPayload(input.id(), textualRepresentation)); + } +} diff --git a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java index 3eb15621b..def9195d8 100644 --- a/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java +++ b/backend/application/syson-application-configuration/src/main/java/org/eclipse/syson/application/services/DetailsViewService.java @@ -39,7 +39,6 @@ import org.eclipse.syson.application.configuration.SysMLv2PropertiesConfigurer; import org.eclipse.syson.form.services.api.IDetailsViewHelpTextProvider; import org.eclipse.syson.services.ImportService; -import org.eclipse.syson.services.UtilService; import org.eclipse.syson.sysml.AcceptActionUsage; import org.eclipse.syson.sysml.ActionUsage; import org.eclipse.syson.sysml.Annotation; @@ -60,6 +59,7 @@ import org.eclipse.syson.sysml.ReferenceSubsetting; import org.eclipse.syson.sysml.ReferenceUsage; import org.eclipse.syson.sysml.Relationship; +import org.eclipse.syson.sysml.ResultExpressionMembership; import org.eclipse.syson.sysml.ReturnParameterMembership; import org.eclipse.syson.sysml.StateDefinition; import org.eclipse.syson.sysml.StateUsage; @@ -70,9 +70,7 @@ import org.eclipse.syson.sysml.Type; import org.eclipse.syson.sysml.ViewUsage; import org.eclipse.syson.sysml.metamodel.services.ElementInitializerSwitch; -import org.eclipse.syson.sysml.textual.SysMLElementSerializer; -import org.eclipse.syson.sysml.textual.SysMLSerializingOptions; -import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver; +import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; /** * Java services needed to execute the AQL expressions used in the {@link SysMLv2PropertiesConfigurer}. @@ -95,9 +93,11 @@ public class DetailsViewService { private final EEnumLiteral unsetEnumLiteral; - private final UtilService utilService; + private final MetamodelQueryElementService metamodelQueryElementService; - public DetailsViewService(List composedAdapterFactoryDescriptors, IFeedbackMessageService feedbackMessageService, IReadOnlyObjectPredicate readOnlyObjectPredicate, List detailsViewHelpTextProviders) { + public DetailsViewService(List composedAdapterFactoryDescriptors, IFeedbackMessageService feedbackMessageService, IReadOnlyObjectPredicate readOnlyObjectPredicate, + MetamodelQueryElementService metamodelQueryElementService, + List detailsViewHelpTextProviders) { this.composedAdapterFactoryDescriptors = Objects.requireNonNull(composedAdapterFactoryDescriptors); this.feedbackMessageService = Objects.requireNonNull(feedbackMessageService); this.readOnlyObjectPredicate = Objects.requireNonNull(readOnlyObjectPredicate); @@ -107,7 +107,7 @@ public DetailsViewService(List composedAdapterFactoryDescriptors, IF this.unsetEnumLiteral = EcoreFactory.eINSTANCE.createEEnumLiteral(); this.unsetEnumLiteral.setName("unset"); this.unsetEnumLiteral.setLiteral("unset"); - this.utilService = new UtilService(); + this.metamodelQueryElementService = Objects.requireNonNull(metamodelQueryElementService); } public String getDetailsViewLabel(Element element, EStructuralFeature eStructuralFeature) { @@ -574,8 +574,8 @@ public Element getFeatureValue(Element self) { Element result = null; if (self instanceof FeatureValue featureValue && featureValue.getValue() != null) { result = featureValue; - } else if (self instanceof Feature feature && this.utilService.getValuation(feature) != null && this.utilService.getValuation(feature).getValue() != null) { - result = this.utilService.getValuation(feature); + } else if (self instanceof Feature feature) { + result = this.metamodelQueryElementService.getValueExpression(feature).orElse(null); } return result; } @@ -589,22 +589,41 @@ public Element getFeatureValue(Element self) { */ public String getValueExpressionTextualRepresentation(FeatureValue featureValue) { Expression value = featureValue.getValue(); - String result = ""; - if (value != null) { - SysMLSerializingOptions options = new SysMLSerializingOptions.Builder() - .lineSeparator("\n") - .nameDeresolver(new FileNameDeresolver()) - .indentation("\t") - .needEscapeCharacter(false) - .build(); - String textualFormat = new SysMLElementSerializer(options, s -> { - // Do nothing for now - }).doSwitch(value); - if (textualFormat != null) { - result = textualFormat; - } - } - return result; + return this.getExpressionAsText(value); + } + + /** + * Gets the textual representation of the value of an actual {@link Expression}. + * + * @param expression + * an {@link Expression} + * @return a textual representation of the expression (or empty string if none) + */ + public String getExpressionTextualRepresentation(Expression expression) { + return this.getExpressionAsText(expression); + } + + /** + * Gets the textual representation of the value of a {@link ResultExpressionMembership}. + * + * @param resultExpression + * a {@link ResultExpressionMembership} + * @return a textual representation of the value (or empty string if none) + */ + public String getResultExpressionTextualRepresentation(ResultExpressionMembership resultExpression) { + Expression value = resultExpression.getOwnedResultExpression(); + return this.getExpressionAsText(value); + } + + /** + * Returns the serialized representation of an expression as plain text. + * + * @param expression + * the Expression + * @return the plain text representation of the expression. + */ + private String getExpressionAsText(Expression expression) { + return this.metamodelQueryElementService.getExpressionTextualRepresentation(expression); } /** diff --git a/backend/application/syson-application-configuration/src/main/resources/schema/expressions.graphqls b/backend/application/syson-application-configuration/src/main/resources/schema/expressions.graphqls new file mode 100644 index 000000000..e02595d6d --- /dev/null +++ b/backend/application/syson-application-configuration/src/main/resources/schema/expressions.graphqls @@ -0,0 +1,15 @@ +extend type EditingContext { + expressionTextualRepresentation(elementId: ID!): String +} + +extend type Mutation { + deleteExpression(input: DeleteExpressionInput!): DeleteExpressionPayload! +} + +input DeleteExpressionInput { + id: ID! + editingContextId: String! + parentElementId: String! +} + +union DeleteExpressionPayload = ErrorPayload | SuccessPayload diff --git a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java index 0f48b5c95..3e85799c0 100644 --- a/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java +++ b/backend/application/syson-application-configuration/src/test/java/org/eclipse/syson/application/services/DetailsViewServiceTest.java @@ -30,6 +30,7 @@ import org.eclipse.syson.sysml.Package; import org.eclipse.syson.sysml.SysmlFactory; import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; import org.eclipse.syson.sysml.util.ElementUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -49,7 +50,7 @@ public void setUp() { // SysON for the moment. List composedAdapterFactoryDescriptors = List.of(); this.detailsViewService = new DetailsViewService(composedAdapterFactoryDescriptors, new IFeedbackMessageService.NoOp(), - new ComposedReadOnlyObjectPredicate(List.of(new SysONReadOnlyObjectPredicateDelegate()), new DefaultReadOnlyObjectPredicate()), List.of()); + new ComposedReadOnlyObjectPredicate(List.of(new SysONReadOnlyObjectPredicateDelegate()), new DefaultReadOnlyObjectPredicate()), new MetamodelQueryElementService(), List.of()); } @Test diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/view/SysONExplorerTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/view/SysONExplorerTests.java index a8e99293e..6247ac35d 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/view/SysONExplorerTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controller/explorer/view/SysONExplorerTests.java @@ -793,7 +793,7 @@ public void sysONExplorerTreeItemContextMenuEntriesTest() { var menuEntriesIds = this.treeItemContextMenuTester.getContextMenuEntries(GeneralViewEmptyTestProjectData.EDITING_CONTEXT, treeId.get(), GeneralViewEmptyTestProjectData.SemanticIds.VIEW_USAGE_ID); // no NewRepresentation on a ViewUsage which already contains a standard diagram or requirements-table - assertThat(menuEntriesIds).hasSize(4) + assertThat(menuEntriesIds).hasSize(5) .contains(ExplorerTreeItemContextMenuEntryProvider.NEW_OBJECT) .contains(SysONExplorerTreeItemContextMenuEntryProvider.NEW_OBJECTS_FROM_TEXT_MENU_ENTRY_CONTRIBUTION_ID) .contains(ExplorerTreeItemContextMenuEntryProvider.EXPAND_ALL); @@ -879,14 +879,7 @@ public void sysONExplorerTreeExpressionLabelTest() { public void sysONExplorerHidesExpressionInternalsByDefault() { List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(ExpressionSamplesProjectData.EDITING_CONTEXT_ID, this.sysONExplorerTreeDescriptionId); - var expandedItemIds = List.of( - ExpressionSamplesProjectData.SemanticIds.EXPRESSIONS_DOCUMENT_ID, - ExpressionSamplesProjectData.SemanticIds.EXPRESSIONS_PACKAGE_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_VALUE_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_PRESSURE_LIMIT_CONSTRAINT_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_PRESSURE_LIMIT_CONSTRAINT_VALUE_ID); + var expandedItemIds = ExpressionSamplesProjectData.SemanticIds.ALL_IDS; var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, expandedItemIds, defaultFilters); var input = new ExplorerEventInput(UUID.randomUUID(), ExpressionSamplesProjectData.EDITING_CONTEXT_ID, explorerRepresentationId); @@ -905,7 +898,7 @@ public void sysONExplorerHidesExpressionInternalsByDefault() { var packageItem = documentItem.getChildren().get(0); assertThat(packageItem.getLabel().toString()).isEqualTo("Expressions"); - assertThat(packageItem.getChildren()).hasSize(7); + assertThat(packageItem.getChildren()).hasSize(6); var tankItem = this.getChildByLabel(packageItem, "Tank"); assertThat(tankItem).isPresent(); @@ -940,14 +933,7 @@ public void sysONExplorerExpressionInternalsCanBeRevealed() { // Keep all defaults but HIDE_EXPRESSION_INTERNALS_ID List activeFilters = defaultFilters.stream().filter(filterId -> !SysONTreeFilterConstants.HIDE_EXPRESSION_INTERNALS_ID.equals(filterId)).toList(); - var expandedItemIds = List.of( - ExpressionSamplesProjectData.SemanticIds.EXPRESSIONS_DOCUMENT_ID, - ExpressionSamplesProjectData.SemanticIds.EXPRESSIONS_PACKAGE_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_VALUE_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_PRESSURE_LIMIT_CONSTRAINT_ID, - ExpressionSamplesProjectData.SemanticIds.TANK_PRESSURE_LIMIT_CONSTRAINT_VALUE_ID); + var expandedItemIds = ExpressionSamplesProjectData.SemanticIds.ALL_IDS; var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, expandedItemIds, activeFilters); var input = new ExplorerEventInput(UUID.randomUUID(), ExpressionSamplesProjectData.EDITING_CONTEXT_ID, explorerRepresentationId); @@ -966,7 +952,7 @@ public void sysONExplorerExpressionInternalsCanBeRevealed() { var packageItem = documentItem.getChildren().get(0); assertThat(packageItem.getLabel().toString()).isEqualTo("Expressions"); - assertThat(packageItem.getChildren()).hasSize(7); + assertThat(packageItem.getChildren()).hasSize(6); var tankItem = this.getChildByLabel(packageItem, "Tank"); assertThat(tankItem).isPresent(); diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/ExpressionsControllersIntegrationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/ExpressionsControllersIntegrationTests.java new file mode 100644 index 000000000..a63ae0383 --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/ExpressionsControllersIntegrationTests.java @@ -0,0 +1,649 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.controllers.expressions; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.sirius.components.trees.tests.TreeEventPayloadConsumer.assertRefreshedTreeThat; + +import com.jayway.jsonpath.JsonPath; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.eclipse.sirius.components.core.api.ErrorPayload; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IIdentityService; +import org.eclipse.sirius.components.core.api.IInput; +import org.eclipse.sirius.components.core.api.IObjectSearchService; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.core.api.SuccessPayload; +import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionInput; +import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload; +import org.eclipse.sirius.components.graphql.tests.api.IExecuteEditingContextFunctionRunner; +import org.eclipse.sirius.web.application.views.explorer.ExplorerEventInput; +import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; +import org.eclipse.sirius.web.tests.services.explorer.ExplorerEventSubscriptionRunner; +import org.eclipse.sirius.web.tests.services.representation.RepresentationIdBuilder; +import org.eclipse.syson.AbstractIntegrationTests; +import org.eclipse.syson.GivenSysONServer; +import org.eclipse.syson.application.controllers.expressions.graphql.CreateExpressionMutationRunner; +import org.eclipse.syson.application.controllers.expressions.graphql.DeleteExpressionMutationRunner; +import org.eclipse.syson.application.controllers.expressions.graphql.EditExpressionMutationRunner; +import org.eclipse.syson.application.controllers.expressions.graphql.ExpressionTextualRepresentationQueryRunner; +import org.eclipse.syson.application.data.ExpressionSamplesProjectData; +import org.eclipse.syson.application.expressions.dto.DeleteExpressionInput; +import org.eclipse.syson.services.explorer.api.IExplorerDefaultFiltersSearchService; +import org.eclipse.syson.sysml.AttributeUsage; +import org.eclipse.syson.sysml.ConstraintUsage; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Expression; +import org.eclipse.syson.sysml.FeatureValue; +import org.eclipse.syson.sysml.TransitionUsage; +import org.eclipse.syson.sysml.dto.CreateExpressionInput; +import org.eclipse.syson.sysml.dto.CreateExpressionSuccessPayload; +import org.eclipse.syson.sysml.dto.EditExpressionInput; +import org.eclipse.syson.sysml.dto.EditExpressionSuccessPayload; +import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; +import org.eclipse.syson.tree.explorer.view.SysONTreeViewDescriptionProvider; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.transaction.TestTransaction; +import org.springframework.transaction.annotation.Transactional; + +import reactor.test.StepVerifier; + +/** + * Tests for GraphQL Queries and Mutations related to SysML Expressions. + * + * @author pcdavid + */ +@Transactional +@SuppressWarnings("checkstyle:MultipleStringLiterals") +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class ExpressionsControllersIntegrationTests extends AbstractIntegrationTests { + @Autowired + private IGivenInitialServerState givenInitialServerState; + + @Autowired + private ExplorerEventSubscriptionRunner explorerEventSubscriptionRunner; + + @Autowired + private RepresentationIdBuilder representationIdBuilder; + + @Autowired + private SysONTreeViewDescriptionProvider sysonTreeViewDescriptionProvider; + + @Autowired + private IExplorerDefaultFiltersSearchService explorerDefaultFiltersSearchService; + + @Autowired + private IObjectSearchService objectSearchService; + + @Autowired + private IIdentityService identityService; + + @Autowired + private IExecuteEditingContextFunctionRunner executeEditingContextFunctionRunner; + + @Autowired + private ExpressionTextualRepresentationQueryRunner expressionTextualRepresentationQueryRunner; + + @Autowired + private CreateExpressionMutationRunner createExpressionMutationRunner; + + @Autowired + private EditExpressionMutationRunner editExpressionMutationRunner; + + @Autowired + private DeleteExpressionMutationRunner deleteExpressionMutationRunner; + + private String sysONExplorerTreeDescriptionId; + + private MetamodelQueryElementService metamodelQueryElementService; + + @BeforeEach + public void beforeEach() { + this.sysONExplorerTreeDescriptionId = this.sysonTreeViewDescriptionProvider.getDescriptionId(); + this.givenInitialServerState.initialize(); + this.metamodelQueryElementService = new MetamodelQueryElementService(); + } + + @DisplayName("GIVEN a SysML attribute which does not have an initial or default value, WHEN creating a new expression on it THEN the new expression is created with proper name resolution") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void canCreateExpressionInEmptyAttribute() { + String editingContextId = ExpressionSamplesProjectData.EDITING_CONTEXT_ID; + + List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(editingContextId, this.sysONExplorerTreeDescriptionId); + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, List.of(), defaultFilters); + var explorerInput = new ExplorerEventInput(UUID.randomUUID(), editingContextId, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(explorerInput).flux(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + }); + + Runnable checkPressureAttributeHasNoValueExpression = this.checkElementHasNoExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.TANK_PRESSURE_ATTRIBUTE_ID, + AttributeUsage.class); + + Runnable createExpressionOnPressureAttribute = this.createExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.TANK_PRESSURE_ATTRIBUTE_ID, "maxPressure / 2"); + + Consumer treeRefreshed = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getId()).isEqualTo(treeId.get()); + }); + + Runnable checkPressureAttributeHasExpectedValueExpression = this.checkElementHasExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.TANK_PRESSURE_ATTRIBUTE_ID, + AttributeUsage.class, + new AtomicReference<>(), "maxPressure / 2"); + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(checkPressureAttributeHasNoValueExpression) + .then(createExpressionOnPressureAttribute) + .consumeNextWith(treeRefreshed) + .then(checkPressureAttributeHasExpectedValueExpression) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a SysML attribute which does have an existing expression, WHEN trying to create a new expression on it THEN the new expression is created with proper name resolution") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void canNotCreateExpressionInNonEmptyAttribute() { + String editingContextId = ExpressionSamplesProjectData.EDITING_CONTEXT_ID; + + List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(editingContextId, this.sysONExplorerTreeDescriptionId); + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, List.of(), defaultFilters); + var explorerInput = new ExplorerEventInput(UUID.randomUUID(), editingContextId, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(explorerInput).flux(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + }); + + Runnable checkMaxVolumeAttributeHasExistingValueExpression = this.checkElementHasExpression(editingContextId, + ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID, AttributeUsage.class, new AtomicReference<>(), + "100.0 * minVolume"); + + Runnable tryCreateExpressionOnMaxVolumnAttribute = () -> { + var input = new CreateExpressionInput(UUID.randomUUID(), editingContextId, ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID, "42"); + var result = this.createExpressionMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.createExpression.__typename"); + assertThat(typename).isEqualTo(ErrorPayload.class.getSimpleName()); + }; + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(checkMaxVolumeAttributeHasExistingValueExpression) + .then(tryCreateExpressionOnMaxVolumnAttribute) + .then(checkMaxVolumeAttributeHasExistingValueExpression) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a SysML attribute that has an existing expression, WHEN editing the expression to a new one THEN the attribute's expression is replaced") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void textEditAttributeExpression() { + String editingContextId = ExpressionSamplesProjectData.EDITING_CONTEXT_ID; + + List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(editingContextId, this.sysONExplorerTreeDescriptionId); + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, List.of(), defaultFilters); + var explorerInput = new ExplorerEventInput(UUID.randomUUID(), editingContextId, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(explorerInput).flux(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + }); + + var existingExpressionId = new AtomicReference(); + + Runnable checkInitialExpression = this.checkElementHasExpression(editingContextId, + ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID, AttributeUsage.class, existingExpressionId, + "100.0 * minVolume"); + + Runnable editExpressionOnMaxVolumeAttribute = this.editExpression(editingContextId, existingExpressionId::get, "50 * minVolume"); + + Consumer treeRefreshed = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getId()).isEqualTo(treeId.get()); + }); + + Runnable checkExpressionUpdated = this.checkElementHasExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID, + AttributeUsage.class, + existingExpressionId, + "50 * minVolume"); + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(checkInitialExpression) + .then(editExpressionOnMaxVolumeAttribute) + .consumeNextWith(treeRefreshed) + .then(checkExpressionUpdated) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a SysML constraint which does not have a predicate expression, WHEN creating a new expression on it THEN the new expression is created with proper name resolution") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void canCreateExpressionInEmptyConstraint() { + String editingContextId = ExpressionSamplesProjectData.EDITING_CONTEXT_ID; + + List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(editingContextId, this.sysONExplorerTreeDescriptionId); + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, List.of(), defaultFilters); + var explorerInput = new ExplorerEventInput(UUID.randomUUID(), editingContextId, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(explorerInput).flux(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + }); + + Runnable checkNoInitialExpression = this.checkElementHasNoExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.PERFORMANCE_CONCERN_ASSUME_ID, + ConstraintUsage.class); + + Runnable createExpression = this.createExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.PERFORMANCE_CONCERN_ASSUME_ID, "s.enabled == true"); + + Consumer treeRefreshed = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getId()).isEqualTo(treeId.get()); + }); + + Runnable checkCreatedExpression = this.checkElementHasExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.PERFORMANCE_CONCERN_ASSUME_ID, ConstraintUsage.class, + new AtomicReference<>(), "s.enabled == true"); + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(checkNoInitialExpression) + .then(createExpression) + .consumeNextWith(treeRefreshed) + .then(checkCreatedExpression) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a SysML transition which does not have a guard expression, WHEN creating a new expression on it THEN the new expression is created with proper name resolution") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void canCreateGuardExpressionInEmptyTransition() { + String editingContextId = ExpressionSamplesProjectData.EDITING_CONTEXT_ID; + + List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(editingContextId, this.sysONExplorerTreeDescriptionId); + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, List.of(), defaultFilters); + var explorerInput = new ExplorerEventInput(UUID.randomUUID(), editingContextId, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(explorerInput).flux(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + }); + + Runnable checkNoInitialExpression = this.checkElementHasNoExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.THERMAL_CONTROL_TO_HEATING_TRANSITION_ID, + TransitionUsage.class); + + Runnable createExpression = this.createExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.THERMAL_CONTROL_TO_HEATING_TRANSITION_ID, "currentTemp < targetTemp - tolerance"); + + Consumer treeRefreshed = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getId()).isEqualTo(treeId.get()); + }); + + Runnable checkCreatedExpression = this.checkElementHasExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.THERMAL_CONTROL_TO_HEATING_TRANSITION_ID, TransitionUsage.class, + new AtomicReference<>(), "currentTemp < targetTemp - tolerance"); + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(checkNoInitialExpression) + .then(createExpression) + .consumeNextWith(treeRefreshed) + .then(checkCreatedExpression) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a SysML transition with a guard expression, WHEN editing the expression on it THEN the new expression is modified with proper name resolution") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void canEditGuardExpressionOnTransition() { + String editingContextId = ExpressionSamplesProjectData.EDITING_CONTEXT_ID; + + List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(editingContextId, this.sysONExplorerTreeDescriptionId); + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, List.of(), defaultFilters); + var explorerInput = new ExplorerEventInput(UUID.randomUUID(), editingContextId, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(explorerInput).flux(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + }); + + var expressionId = new AtomicReference(); + + Runnable checkNoInitialExpression = this.checkElementHasExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.THERMAL_CONTROL_TO_COOLING_TRANSITION_ID, + TransitionUsage.class, expressionId, "currentTemp > targetTemp + tolerance"); + + Runnable createExpression = this.editExpression(editingContextId, expressionId::get, "currentTemp >= targetTemp + (tolerance * 0.9)"); + + Consumer treeRefreshed = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getId()).isEqualTo(treeId.get()); + }); + + Runnable checkCreatedExpression = this.checkElementHasExpression(editingContextId, ExpressionSamplesProjectData.SemanticIds.THERMAL_CONTROL_TO_COOLING_TRANSITION_ID, TransitionUsage.class, + new AtomicReference<>(), "currentTemp >= targetTemp + tolerance * 0.9"); // Note that the "()" from the + // source are lost in the + // serialization + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(checkNoInitialExpression) + .then(createExpression) + .consumeNextWith(treeRefreshed) + .then(checkCreatedExpression) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a SysML attribute that has an existing expression, WHEN editing the expression to an invalid new value THEN the attribute's expression is not modified") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void editAttributeExpressionWithInvalidNewValue() { + String editingContextId = ExpressionSamplesProjectData.EDITING_CONTEXT_ID; + + List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(editingContextId, this.sysONExplorerTreeDescriptionId); + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, List.of(), defaultFilters); + var explorerInput = new ExplorerEventInput(UUID.randomUUID(), editingContextId, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(explorerInput).flux(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + }); + + var existingExpressionId = new AtomicReference(); + + Runnable checkMaxVolumeAttributeHasExistingValueExpression = this.semanticCheck(editingContextId, (editingContext, input) -> { + var optionalMaxVolumeAttribute = this.objectSearchService.getObject(editingContext, ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID); + assertThat(optionalMaxVolumeAttribute).containsInstanceOf(AttributeUsage.class); + var maxVolumeAttribute = (AttributeUsage) optionalMaxVolumeAttribute.get(); + Optional valueExpression = this.metamodelQueryElementService.getValueExpression(maxVolumeAttribute); + assertThat(valueExpression).isNotEmpty(); + assertThat(this.metamodelQueryElementService.getExpressionTextualRepresentation(valueExpression.get())).isEqualTo("100.0 * minVolume"); + existingExpressionId.set(this.identityService.getId(valueExpression.get())); + return new ExecuteEditingContextFunctionSuccessPayload(input.id(), optionalMaxVolumeAttribute.get()); + }); + + Runnable editExpressionOnMaxVolumeAttributeWithInvalidValue = () -> { + var input = new EditExpressionInput(UUID.randomUUID(), editingContextId, existingExpressionId.get(), "50 * minVolumeTypo"); + var result = this.editExpressionMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.editExpression.__typename"); + assertThat(typename).isEqualTo(ErrorPayload.class.getSimpleName()); + String messageLevel = JsonPath.read(result.data(), "$.data.editExpression.messages[0].level"); + assertThat(messageLevel).isEqualTo("WARNING"); + String messageBody = JsonPath.read(result.data(), "$.data.editExpression.messages[0].body"); + assertThat(messageBody).contains("Unable to resolve name 'minVolumeTypo' for reference 'memberElement' on element '[Membership] Expressions::Tank::maxVolume"); + }; + + Runnable checkMaxVolumeAttributeHasInitialValueExpression = this.semanticCheck(editingContextId, (editingContext, input) -> { + var optionalMaxVolumeAttribute = this.objectSearchService.getObject(editingContext, ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID); + assertThat(optionalMaxVolumeAttribute).containsInstanceOf(AttributeUsage.class); + var maxVolumeAttribute = (AttributeUsage) optionalMaxVolumeAttribute.get(); + Optional valueExpression = this.metamodelQueryElementService.getValueExpression(maxVolumeAttribute); + assertThat(valueExpression).isNotEmpty(); + assertThat(this.metamodelQueryElementService.getExpressionTextualRepresentation(valueExpression.get())).isEqualTo("100.0 * minVolume"); + // Check this is indeed the exact same element as before + String newValueExpressionId = this.identityService.getId(valueExpression.get()); + assertThat(newValueExpressionId).isEqualTo(existingExpressionId.get()); + + // Make sure we did not create an additional invalid FeatureValue and left it around. + assertThat(maxVolumeAttribute.getOwnedRelationship().stream().filter(FeatureValue.class::isInstance)).hasSize(1); + + return new ExecuteEditingContextFunctionSuccessPayload(input.id(), optionalMaxVolumeAttribute.get()); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(checkMaxVolumeAttributeHasExistingValueExpression) + .then(editExpressionOnMaxVolumeAttributeWithInvalidValue) + .then(checkMaxVolumeAttributeHasInitialValueExpression) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN an attribute with an expression, WHEN invoking delete expression on it THEN the expression and its owning relationship are removed from the model") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void deleteExpressionFromParentAttribute() { + this.deleteExistingExpressionFromParentElement(ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID); + } + + @DisplayName("GIVEN an assume constraint in a concern with an expression, WHEN invoking delete expression on it THEN the expression and its owning relationship are removed from the model") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void deleteExpressionFromParentConstraint() { + this.deleteExistingExpressionFromParentElement(ExpressionSamplesProjectData.SemanticIds.PERFORMANCE_CONCERN_REQUIRE_ID); + } + + @DisplayName("GIVEN a transition with a guard expression, WHEN invoking delete expression on it THEN the expression and its owning relationship are removed from the model") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void deleteExpressionFromParentTransition() { + this.deleteExistingExpressionFromParentElement(ExpressionSamplesProjectData.SemanticIds.THERMAL_CONTROL_TO_COOLING_TRANSITION_ID); + } + + /** + * Invoke the {@code deleteExpression()} mutation on a parent element which contains a top-level expression + * definition, and checks that both the expression and its owning relationship are correctly removed from the model. + * + * @param parentElementId + * the id of the parent element of the expression to remove. + */ + public void deleteExistingExpressionFromParentElement(String parentElementId) { + String editingContextId = ExpressionSamplesProjectData.EDITING_CONTEXT_ID; + + List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(editingContextId, this.sysONExplorerTreeDescriptionId); + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, List.of(), defaultFilters); + var explorerInput = new ExplorerEventInput(UUID.randomUUID(), editingContextId, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(explorerInput).flux(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + }); + + var initialExpressionId = new AtomicReference(); + var initialExpressionOwningRelationshipId = new AtomicReference(); + + Runnable checkParentElementExistsAndHasExpression = this.semanticCheck(editingContextId, (editingContext, input) -> { + var optionalParentElement = this.objectSearchService.getObject(editingContext, parentElementId); + assertThat(optionalParentElement).containsInstanceOf(Element.class); + var parentElement = (Element) optionalParentElement.get(); + Optional optionalExpression = this.metamodelQueryElementService.findSingleExpressionDefinition(parentElement); + assertThat(optionalExpression).containsInstanceOf(Expression.class); + initialExpressionId.set(this.identityService.getId(optionalExpression.get())); + initialExpressionOwningRelationshipId.set(this.identityService.getId(optionalExpression.get().getOwningRelationship())); + return new ExecuteEditingContextFunctionSuccessPayload(input.id(), optionalParentElement.get()); + }); + + Runnable deleteExpressionFromParentElement = () -> { + var input = new DeleteExpressionInput(UUID.randomUUID(), editingContextId, parentElementId); + var result = this.deleteExpressionMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.deleteExpression.__typename"); + assertThat(typename).isEqualTo(SuccessPayload.class.getSimpleName()); + }; + + Consumer treeRefreshed = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + assertThat(tree.getId()).isEqualTo(treeId.get()); + }); + + Runnable checkParentElementHasNoExpression = this.semanticCheck(editingContextId, (editingContext, input) -> { + var optionalParentElement = this.objectSearchService.getObject(editingContext, parentElementId); + assertThat(optionalParentElement).containsInstanceOf(Element.class); + var parentElement = (Element) optionalParentElement.get(); + Optional optionalExpression = this.metamodelQueryElementService.findSingleExpressionDefinition(parentElement); + assertThat(optionalExpression).isEmpty(); + + // The original Expression element and its Relationship should no longer be part of the model + assertThat(this.objectSearchService.getObject(editingContext, initialExpressionId.get())).isEmpty(); + assertThat(this.objectSearchService.getObject(editingContext, initialExpressionOwningRelationshipId.get())).isEmpty(); + + return new ExecuteEditingContextFunctionSuccessPayload(input.id(), parentElement); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(checkParentElementExistsAndHasExpression) + .then(deleteExpressionFromParentElement) + .consumeNextWith(treeRefreshed) + .then(checkParentElementHasNoExpression) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a SysML model with expressions, WHEN requesting the textual representation of an actual top-level Expression element, THEN its textual representation is returned") + @GivenSysONServer({ ExpressionSamplesProjectData.SCRIPT_PATH }) + @Test + public void topLevelExpressionTextualRepresentation() { + String editingContextId = ExpressionSamplesProjectData.EDITING_CONTEXT_ID; + + List defaultFilters = this.explorerDefaultFiltersSearchService.findTreeDefaultFilterIds(editingContextId, this.sysONExplorerTreeDescriptionId); + var explorerRepresentationId = this.representationIdBuilder.buildExplorerRepresentationId(this.sysONExplorerTreeDescriptionId, List.of(), defaultFilters); + var input = new ExplorerEventInput(UUID.randomUUID(), editingContextId, explorerRepresentationId); + var flux = this.explorerEventSubscriptionRunner.run(input).flux(); + TestTransaction.flagForCommit(); + TestTransaction.end(); + + var treeId = new AtomicReference(); + Consumer initialTreeContentConsumer = assertRefreshedTreeThat(tree -> { + assertThat(tree).isNotNull(); + treeId.set(tree.getId()); + }); + + // The Tank part and its attribute are not themselves expressions => "" + var checkTank = this.checkExpressiontTextualRepresentation(editingContextId, ExpressionSamplesProjectData.SemanticIds.TANK_ID, ""); + var checkTankAttribute = this.checkExpressiontTextualRepresentation(editingContextId, ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_ID, ""); + // The actual attribute default value expression however should be correctly represented + var checkTankAttributeValueExpression = this.checkExpressiontTextualRepresentation(editingContextId, ExpressionSamplesProjectData.SemanticIds.TANK_MAX_VOLUME_ATTRIBUTE_VALUE_ID, + "100.0 * minVolume"); + + var checkPerformanceConcern = this.checkExpressiontTextualRepresentation(editingContextId, ExpressionSamplesProjectData.SemanticIds.PERFORMANCE_CONCERN_ID, ""); + // A ConstaintUsage *is* an Expression from the point of view of SysML's type hierarchy, but not a top-level + // Expression, so we expect "" here. + var checkPerformanceConcernAssumeConstraint = this.checkExpressiontTextualRepresentation(editingContextId, ExpressionSamplesProjectData.SemanticIds.PERFORMANCE_CONCERN_ASSUME_ID, ""); + var checkPerformanceConcernRequireConstraint = this.checkExpressiontTextualRepresentation(editingContextId, ExpressionSamplesProjectData.SemanticIds.PERFORMANCE_CONCERN_REQUIRE_ID, ""); + // require s.samplingRate >= 50.0 & s.currentValue != 0.0 | s.errorCount == 0 + var checkPerformanceConcernRequireConstraintExpression = this.checkExpressiontTextualRepresentation(editingContextId, + ExpressionSamplesProjectData.SemanticIds.PERFORMANCE_CONCERN_REQUIRE_EXPRESSION_ID, "s.samplingRate >= 50.0 & s.currentValue != 0.0 | s.errorCount == 0"); + + StepVerifier.create(flux) + .consumeNextWith(initialTreeContentConsumer) + .then(checkTank) + .then(checkTankAttribute) + .then(checkTankAttributeValueExpression) + .then(checkPerformanceConcern) + .then(checkPerformanceConcernAssumeConstraint) + .then(checkPerformanceConcernRequireConstraint) + .then(checkPerformanceConcernRequireConstraintExpression) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + + /** + * Executes a function in the editing context with the specified id (which is assumed to be loaded). The function + * can perform JUnit assertions to verify the state of the semantic model in the editing context. + */ + private Runnable semanticCheck(String editingContextId, BiFunction checker) { + return () -> { + var input = new ExecuteEditingContextFunctionInput(UUID.randomUUID(), editingContextId, checker); + var payload = this.executeEditingContextFunctionRunner.execute(input).block(); + assertThat(payload).isInstanceOf(ExecuteEditingContextFunctionSuccessPayload.class); + }; + } + + private Runnable createExpression(String editingContextId, String parentElementId, String expressionContent) { + return () -> { + var input = new CreateExpressionInput(UUID.randomUUID(), editingContextId, parentElementId, expressionContent); + var result = this.createExpressionMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.createExpression.__typename"); + assertThat(typename).isEqualTo(CreateExpressionSuccessPayload.class.getSimpleName()); + }; + } + + private Runnable editExpression(String editingContextId, Supplier elementId, String expressionContent) { + return () -> { + var input = new EditExpressionInput(UUID.randomUUID(), editingContextId, elementId.get(), expressionContent); + var result = this.editExpressionMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.editExpression.__typename"); + assertThat(typename).isEqualTo(EditExpressionSuccessPayload.class.getSimpleName()); + }; + } + + private Runnable checkExpressiontTextualRepresentation(String editingContextId, String elementId, String expectedTextualRepresentation) { + return () -> { + Map variables = Map.of("editingContextId", editingContextId, "elementId", elementId); + var result = this.expressionTextualRepresentationQueryRunner.run(variables); + String textualRepresentation = JsonPath.read(result.data(), "$.data.viewer.editingContext.expressionTextualRepresentation"); + assertThat(textualRepresentation).as("elementId: {}", elementId).isEqualTo(expectedTextualRepresentation); + }; + } + + private Runnable checkElementHasNoExpression(String editingContextId, String elementId, Class expectedElementType) { + return this.semanticCheck(editingContextId, (editingContext, input) -> { + var optionalElement = this.objectSearchService.getObject(editingContext, elementId); + assertThat(optionalElement).containsInstanceOf(expectedElementType); + T element = expectedElementType.cast(optionalElement.get()); + Optional valueExpression = this.metamodelQueryElementService.findSingleExpressionDefinition(element); + assertThat(valueExpression).isEmpty(); + return new ExecuteEditingContextFunctionSuccessPayload(input.id(), optionalElement.get()); + }); + } + + private Runnable checkElementHasExpression(String editingContextId, String elementId, Class expectedElementType, AtomicReference expressionId, + String expectedExpressionTextualRepresentation) { + return this.semanticCheck(editingContextId, (editingContext, input) -> { + var optionalElement = this.objectSearchService.getObject(editingContext, elementId); + assertThat(optionalElement).containsInstanceOf(expectedElementType); + T element = expectedElementType.cast(optionalElement.get()); + Optional valueExpression = this.metamodelQueryElementService.findSingleExpressionDefinition(element); + assertThat(valueExpression).isNotEmpty(); + expressionId.set(this.identityService.getId(valueExpression.get())); + assertThat(this.metamodelQueryElementService.getExpressionTextualRepresentation(valueExpression.get())).isEqualTo(expectedExpressionTextualRepresentation); + return new ExecuteEditingContextFunctionSuccessPayload(input.id(), optionalElement.get()); + }); + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/CreateExpressionMutationRunner.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/CreateExpressionMutationRunner.java new file mode 100644 index 000000000..6b654d992 --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/CreateExpressionMutationRunner.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.controllers.expressions.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.graphql.tests.api.GraphQLResult; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.eclipse.syson.sysml.dto.CreateExpressionInput; +import org.springframework.stereotype.Service; + +/** + * Used to invoke the createExpression mutation via the GraphQL API. + * + * @author pcdavid + */ +@Service +public class CreateExpressionMutationRunner implements IMutationRunner { + + private static final String CREATE_EXPRESSION_MUTATION = """ + mutation createExpression($input: CreateExpressionInput!) { + createExpression(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on CreateExpressionSuccessPayload { + newExpressionId + messages { + body + level + } + } + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public CreateExpressionMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public GraphQLResult run(CreateExpressionInput input) { + return this.graphQLRequestor.execute(CREATE_EXPRESSION_MUTATION, input); + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/DeleteExpressionMutationRunner.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/DeleteExpressionMutationRunner.java new file mode 100644 index 000000000..abd7b616e --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/DeleteExpressionMutationRunner.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.controllers.expressions.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.graphql.tests.api.GraphQLResult; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.eclipse.syson.application.expressions.dto.DeleteExpressionInput; +import org.springframework.stereotype.Service; + +/** + * Used to invoke the deleteExpression mutation via the GraphQL API. + * + * @author pcdavid + */ +@Service +public class DeleteExpressionMutationRunner implements IMutationRunner { + + private static final String DELETE_EXPRESSION_MUTATION = """ + mutation deleteExpression($input: DeleteExpressionInput!) { + deleteExpression(input: $input) { + __typename + ... on SuccessPayload { + messages { + body + level + } + } + ... on ErrorPayload { + messages { + body + level + } + } + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public DeleteExpressionMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public GraphQLResult run(DeleteExpressionInput input) { + return this.graphQLRequestor.execute(DELETE_EXPRESSION_MUTATION, input); + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/EditExpressionMutationRunner.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/EditExpressionMutationRunner.java new file mode 100644 index 000000000..cb8e43023 --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/EditExpressionMutationRunner.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.controllers.expressions.graphql; + +import java.util.Objects; + +import org.eclipse.sirius.components.graphql.tests.api.GraphQLResult; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IMutationRunner; +import org.eclipse.syson.sysml.dto.EditExpressionInput; +import org.springframework.stereotype.Service; + +/** + * Used to invoke the editExpression mutation via the GraphQL API. + * + * @author pcdavid + */ +@Service +public class EditExpressionMutationRunner implements IMutationRunner { + + private static final String EDIT_EXPRESSION_MUTATION = """ + mutation editExpression($input: EditExpressionInput!) { + editExpression(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on EditExpressionSuccessPayload { + newExpressionId + messages { + body + level + } + } + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public EditExpressionMutationRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public GraphQLResult run(EditExpressionInput input) { + return this.graphQLRequestor.execute(EDIT_EXPRESSION_MUTATION, input); + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/ExpressionTextualRepresentationQueryRunner.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/ExpressionTextualRepresentationQueryRunner.java new file mode 100644 index 000000000..c279d886b --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/expressions/graphql/ExpressionTextualRepresentationQueryRunner.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.controllers.expressions.graphql; + +import java.util.Map; +import java.util.Objects; + +import org.eclipse.sirius.components.graphql.tests.api.GraphQLResult; +import org.eclipse.sirius.components.graphql.tests.api.IGraphQLRequestor; +import org.eclipse.sirius.components.graphql.tests.api.IQueryRunner; +import org.springframework.stereotype.Service; + +/** + * User to fetch the textual representation of an expression. + * + * @author pcdavid + */ +@Service +public class ExpressionTextualRepresentationQueryRunner implements IQueryRunner { + private static final String QUERY = """ + query getExpressionTextualRepresentation($editingContextId: ID!, $elementId: ID!) { + viewer { + editingContext(editingContextId: $editingContextId) { + expressionTextualRepresentation(elementId: $elementId) + } + } + } + """; + + private final IGraphQLRequestor graphQLRequestor; + + public ExpressionTextualRepresentationQueryRunner(IGraphQLRequestor graphQLRequestor) { + this.graphQLRequestor = Objects.requireNonNull(graphQLRequestor); + } + + @Override + public GraphQLResult run(Map variables) { + return this.graphQLRequestor.execute(QUERY, variables); + } +} diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ExpressionSamplesProjectData.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ExpressionSamplesProjectData.java index cd12d1aad..4297d5cc2 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ExpressionSamplesProjectData.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/ExpressionSamplesProjectData.java @@ -12,6 +12,8 @@ *******************************************************************************/ package org.eclipse.syson.application.data; +import java.util.List; + /** * Ids for project "Expression-Samples". * @@ -32,6 +34,8 @@ public static final class SemanticIds { public static final String TANK_ID = "2bde22f7-a834-4afa-84bf-1bae1bb434d8"; + public static final String TANK_PRESSURE_ATTRIBUTE_ID = "7479570e-234f-4603-a764-d45ba1228aad"; + public static final String TANK_MAX_VOLUME_ATTRIBUTE_ID = "d9926727-7378-4177-a940-2fb6c1c89dce"; public static final String TANK_MAX_VOLUME_ATTRIBUTE_VALUE_ID = "a9216e54-f44c-4b1f-b262-13650324d325"; @@ -40,5 +44,38 @@ public static final class SemanticIds { public static final String TANK_PRESSURE_LIMIT_CONSTRAINT_VALUE_ID = "faa3b115-5b07-4ecf-9147-0d58ceffaf9c"; + public static final String PERFORMANCE_CONCERN_ID = "da2296a4-25ed-4f04-abc8-d47f93cd223c"; + + public static final String PERFORMANCE_CONCERN_ASSUME_ID = "4fd2c402-15b7-4546-ad9c-7aa9419a1528"; + + public static final String PERFORMANCE_CONCERN_REQUIRE_ID = "6891b4f5-6f02-4c73-ae23-db88ad10b253"; + + public static final String PERFORMANCE_CONCERN_REQUIRE_EXPRESSION_ID = "6f2e1644-b09d-46a2-b155-fc123e0f2953"; + + public static final String THERMAL_CONTROL_STATE_DEFINITION_ID = "2efcf5d4-4948-4b7d-b779-f3f694eb8165"; + + public static final String THERMAL_CONTROL_TO_HEATING_TRANSITION_ID = "0b517687-ef3f-4057-9ce5-27e27f627a22"; + + public static final String THERMAL_CONTROL_TO_COOLING_TRANSITION_ID = "e1c27d0b-476e-42d4-9b81-b5f064d9cbbe"; + + public static final String THERMAL_CONTROL_TO_COOLING_TRANSITION_GUARD_EXPRESSION_ID = "ec67ead5-7739-4dd3-a82e-f1bb5ef34268"; + + public static final List ALL_IDS = List.of( + EXPRESSIONS_DOCUMENT_ID, + EXPRESSIONS_PACKAGE_ID, + TANK_ID, + TANK_PRESSURE_ATTRIBUTE_ID, + TANK_MAX_VOLUME_ATTRIBUTE_ID, + TANK_MAX_VOLUME_ATTRIBUTE_VALUE_ID, + TANK_PRESSURE_LIMIT_CONSTRAINT_ID, + TANK_PRESSURE_LIMIT_CONSTRAINT_VALUE_ID, + PERFORMANCE_CONCERN_ID, + PERFORMANCE_CONCERN_ASSUME_ID, + PERFORMANCE_CONCERN_REQUIRE_ID, + PERFORMANCE_CONCERN_REQUIRE_EXPRESSION_ID, + THERMAL_CONTROL_STATE_DEFINITION_ID, + THERMAL_CONTROL_TO_HEATING_TRANSITION_ID, + THERMAL_CONTROL_TO_COOLING_TRANSITION_ID, + THERMAL_CONTROL_TO_COOLING_TRANSITION_GUARD_EXPRESSION_ID); } } diff --git a/backend/application/syson-application/src/test/resources/scripts/database-content/ExpressionSamples.sql b/backend/application/syson-application/src/test/resources/scripts/database-content/ExpressionSamples.sql index bce471713..028199b38 100644 --- a/backend/application/syson-application/src/test/resources/scripts/database-content/ExpressionSamples.sql +++ b/backend/application/syson-application/src/test/resources/scripts/database-content/ExpressionSamples.sql @@ -27,7 +27,7 @@ INSERT INTO public.semantic_data (id, created_on, last_modified_on) VALUES ('aac -- Data for Name: document; Type: TABLE DATA; Schema: public; Owner: dbuser -- -INSERT INTO public.document (id, semantic_data_id, name, content, created_on, last_modified_on, is_read_only) VALUES ('ef3e6929-0415-4295-b42f-64bed8bd4f55', 'aac82b89-8d53-4b16-91b4-deccc180ba62', 'expressions.sysml', '{"json":{"version":"1.0","encoding":"utf-8"},"ns":{"sysml":"http://www.eclipse.org/syson/sysml"},"migration":{"lastMigrationPerformed":"DiagramStyleDescriptionAddMigrationParticipant","migrationVersion":"2026.5.0-202603171430"},"content":[{"id":"152e5a5b-2b71-4653-8b7f-d46df76cd595","eClass":"sysml:Namespace","data":{"eAnnotations":[{"source":"org.eclipse.syson.sysml.imported"}],"elementId":"cfa2873f-f2f4-475a-8817-9f73e2dff74f","ownedRelationship":[{"id":"bea4a5f8-cc39-4cc0-ab5c-226fcf86f737","eClass":"sysml:OwningMembership","data":{"elementId":"eeda408c-b3e7-41af-a52b-3463777d41c0","ownedRelatedElement":[{"id":"baf5ea6a-7861-4b92-8be2-3fe7a2ebc415","eClass":"sysml:Package","data":{"declaredName":"Expressions","elementId":"6ff78b89-10b8-44f5-817f-50ea23e0c620","ownedRelationship":[{"id":"d8072b8b-400f-4968-b1e2-6601ba10bdc7","eClass":"sysml:NamespaceImport","data":{"elementId":"f2a55343-1f9c-4cc6-9912-8ca86dab734e","importedNamespace":"sysml:LibraryPackage kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#40bb440c-5036-58e1-8675-5afccb8b8f1d"}},{"id":"63ed369c-92bd-48cf-ba76-147c1ec13316","eClass":"sysml:OwningMembership","data":{"elementId":"033678d4-289b-46d1-9704-d97a006fb024","ownedRelatedElement":[{"id":"2bde22f7-a834-4afa-84bf-1bae1bb434d8","eClass":"sysml:PartDefinition","data":{"declaredName":"Tank","elementId":"e5e396e4-635f-44a2-8d85-50e19b94a6e7","ownedRelationship":[{"id":"bd7b6c14-0803-423c-b5d9-f368d8c32e6d","eClass":"sysml:FeatureMembership","data":{"elementId":"66064f96-7440-4236-bb21-11bb8c61224c","ownedRelatedElement":[{"id":"15ccc1e3-16b9-4ddf-9959-43141f32d48e","eClass":"sysml:AttributeUsage","data":{"declaredName":"pressure","elementId":"7479570e-234f-4603-a764-d45ba1228aad","ownedRelationship":[{"id":"b4f4a75e-cc97-4a5a-a6a6-5bfafd5eede6","eClass":"sysml:FeatureValue","data":{"elementId":"3091dd86-0287-4371-aafd-db02ebc46345","ownedRelatedElement":[{"id":"1eae8ba0-03c9-4ff0-87a7-578abde5550f","eClass":"sysml:LiteralRational","data":{"elementId":"c62ef353-15a6-482e-8abe-3008cbc989cc"}}],"isInitial":true}},{"id":"470e47da-8c4f-45be-a841-5bb4fa82f8da","eClass":"sysml:FeatureTyping","data":{"elementId":"4f77e08f-1a50-4056-a6cb-283b6c0de857","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"15ccc1e3-16b9-4ddf-9959-43141f32d48e"}}],"isComposite":true}}]}},{"id":"27c51649-9b3b-471c-b846-33f10fe82332","eClass":"sysml:FeatureMembership","data":{"elementId":"747b5a9e-95de-4908-a7f0-3917cb4385ae","ownedRelatedElement":[{"id":"89b903a2-10c2-42a7-8307-b8bc9a4ba76a","eClass":"sysml:AttributeUsage","data":{"declaredName":"maxPressure","elementId":"058dca9f-5c49-4ae6-8f71-5e11df5b4021","ownedRelationship":[{"id":"01c173ba-5433-45f6-a9ad-5abf786562c9","eClass":"sysml:FeatureValue","data":{"elementId":"2a6cd6e7-6af0-426b-b6e9-1749f4cae266","ownedRelatedElement":[{"id":"703297e9-e397-496c-ac9a-49b2afff4ba7","eClass":"sysml:LiteralRational","data":{"elementId":"6a768b6c-2898-4dfa-aa7c-2a7f3a9fd6e4","value":600.0}}]}},{"id":"bb766cff-de2c-469f-90b8-06bd35514038","eClass":"sysml:FeatureTyping","data":{"elementId":"9e91a82c-3c8b-43f5-96eb-a92781f5a8d8","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"89b903a2-10c2-42a7-8307-b8bc9a4ba76a"}}],"isComposite":true}}]}},{"id":"77e243ce-1879-49a5-a4e6-ec8d91b8da9f","eClass":"sysml:FeatureMembership","data":{"elementId":"2a061597-9b75-422f-ac99-17317f4076a9","ownedRelatedElement":[{"id":"7c2ed44f-f991-4559-8594-cfd844160744","eClass":"sysml:AttributeUsage","data":{"declaredName":"volume","elementId":"87f09d7d-87a8-4edf-8c96-eb7d13c44702","ownedRelationship":[{"id":"9789b076-4604-4407-b662-6179ab76b2c5","eClass":"sysml:FeatureValue","data":{"elementId":"7625d338-d5ed-4050-bf6a-a22c75ea21af","ownedRelatedElement":[{"id":"a512cb6b-9395-48b9-88ba-fcb8b2754484","eClass":"sysml:LiteralRational","data":{"elementId":"cfa46233-fa49-4eef-b384-fc4a5861c39a","value":500.0}}]}},{"id":"91584942-a0c5-46f9-bd52-c252b9c0a285","eClass":"sysml:FeatureTyping","data":{"elementId":"cce82e52-823f-4f33-85ac-765af147c2f8","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"7c2ed44f-f991-4559-8594-cfd844160744"}}],"isComposite":true}}]}},{"id":"f5d0e91d-5d51-490e-93cf-42662bdb62e8","eClass":"sysml:FeatureMembership","data":{"elementId":"a3c78e1d-535a-4e89-a9cf-d97a00080b81","ownedRelatedElement":[{"id":"e7292249-00ff-41d1-a8b5-783668e88cc0","eClass":"sysml:AttributeUsage","data":{"declaredName":"minVolume","elementId":"6e6f68f1-9144-46ab-8b05-50d964f904a6","ownedRelationship":[{"id":"4c81d9e3-e5c9-4320-9cb1-395c353a73d1","eClass":"sysml:FeatureValue","data":{"elementId":"f861ccab-1624-4cee-ad26-1dfdc733163c","ownedRelatedElement":[{"id":"8e41e070-5926-4d74-9c76-d727d69d5fde","eClass":"sysml:LiteralRational","data":{"elementId":"d9800108-0bfd-4553-bd4a-ef16d51c1b38","value":10.0}}]}},{"id":"fcbae3ff-aeb7-48b2-97a0-55f8aa446d89","eClass":"sysml:FeatureTyping","data":{"elementId":"17d1e180-0632-4bec-9c10-0b166eb462f0","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"e7292249-00ff-41d1-a8b5-783668e88cc0"}}],"isComposite":true}}]}},{"id":"4c5f7f98-0312-4d3b-b2ac-b5a05fdb5e2f","eClass":"sysml:FeatureMembership","data":{"elementId":"b77a0d5e-7cf2-4978-a622-420c88455097","ownedRelatedElement":[{"id":"d9926727-7378-4177-a940-2fb6c1c89dce","eClass":"sysml:AttributeUsage","data":{"declaredName":"maxVolume","elementId":"c957466a-8bd4-448c-b2e5-545af7d9cadc","ownedRelationship":[{"id":"8f9592ef-9513-4025-ab13-e7d7f822f7d8","eClass":"sysml:FeatureValue","data":{"elementId":"5fd01a42-4bde-4a47-8b52-54a7c60049c1","ownedRelatedElement":[{"id":"a9216e54-f44c-4b1f-b262-13650324d325","eClass":"sysml:OperatorExpression","data":{"elementId":"ba3cea85-2725-4b96-a2af-a9159e84a7a9","ownedRelationship":[{"id":"b88c3a9d-0ce2-426e-a333-77225c45d429","eClass":"sysml:ParameterMembership","data":{"elementId":"4b58a948-936f-4567-9cb5-77739aee17c3","ownedRelatedElement":[{"id":"1ae9086c-60a8-4e6b-a24f-db44e021761b","eClass":"sysml:Feature","data":{"elementId":"19bfc3af-4154-4848-bd2e-59a1754b47df","ownedRelationship":[{"id":"52bad1f8-ee55-4bd5-aa2b-db12ec015c26","eClass":"sysml:FeatureValue","data":{"elementId":"0b26ad14-8872-4a9b-ac55-dac0f189df10","ownedRelatedElement":[{"id":"01ee6181-e890-4277-93fb-4bccf84bf3e6","eClass":"sysml:LiteralRational","data":{"elementId":"eb76a535-cb0d-4efd-b0f7-430b46d85d1a","value":100.0}}]}}],"direction":"in"}}]}},{"id":"8ed9cb6c-2fb5-43af-9bcf-9f2b83ea6e1b","eClass":"sysml:ParameterMembership","data":{"elementId":"c1df91fb-cea4-45f0-80a3-191171afb130","ownedRelatedElement":[{"id":"0222cbc2-571d-41ca-9aa6-5b7128062066","eClass":"sysml:Feature","data":{"elementId":"22361eb7-6a20-4f84-853a-cc4373032846","ownedRelationship":[{"id":"b31231a4-f853-4b71-ace4-eaab66065097","eClass":"sysml:FeatureValue","data":{"elementId":"49b00782-9d36-43a2-8a66-04d1e739daba","ownedRelatedElement":[{"id":"772d1b21-03f3-4299-aac5-564d787129b5","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"74181cea-a48b-4dbc-a45b-878f026c065f","ownedRelationship":[{"id":"576e21f5-e5b3-4a54-b6a7-636f4f2b7034","eClass":"sysml:Membership","data":{"elementId":"ef7b0f68-6c53-4d49-9537-eb3a561b1d91","memberElement":"e7292249-00ff-41d1-a8b5-783668e88cc0"}}]}}]}}],"direction":"in"}}]}}],"operator":"*"}}]}},{"id":"5f49fe65-264b-4773-9aa5-48ed839d4a86","eClass":"sysml:FeatureTyping","data":{"elementId":"c9ba2582-5b2d-4339-a4d9-edf1d05a350c","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"d9926727-7378-4177-a940-2fb6c1c89dce"}}],"isComposite":true}}]}},{"id":"bcb92dbe-1c98-4fbc-89bf-6a420f65d587","eClass":"sysml:FeatureMembership","data":{"elementId":"50960f37-4657-4ff4-9b24-01cf5d4839d5","ownedRelatedElement":[{"id":"7d42ee06-3c27-4eaa-9438-344fc789906a","eClass":"sysml:ConstraintUsage","data":{"declaredName":"pressureLimit","elementId":"0349f315-d32d-43e9-9627-9228da3bb552","ownedRelationship":[{"id":"d40b295e-159f-44fe-84a8-fa114dac3472","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"27e134d7-60b2-4a41-a2eb-ed1ca6c1a119","ownedRelatedElement":[{"id":"faa3b115-5b07-4ecf-9147-0d58ceffaf9c","eClass":"sysml:OperatorExpression","data":{"elementId":"d909007a-ddd7-4e31-b17a-4653dfa8cef8","ownedRelationship":[{"id":"94611d95-3c3d-45a8-813a-3b35933576e0","eClass":"sysml:ParameterMembership","data":{"elementId":"55d9251d-9722-4f1f-ade8-5cce8d7bafb1","ownedRelatedElement":[{"id":"a6e4adef-1a08-4384-880e-95da452d581a","eClass":"sysml:Feature","data":{"elementId":"d1eb3702-b4d0-48d4-90df-5354359f0d33","ownedRelationship":[{"id":"ce942576-087e-4f35-a0a3-7c23a7fe2bb0","eClass":"sysml:FeatureValue","data":{"elementId":"303c1de0-a072-43e8-b687-38527c51eb0e","ownedRelatedElement":[{"id":"83bc616a-bfb5-4777-a169-ac1f8142d2aa","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"046cd544-1c5c-450f-8fac-3077ff9d3652","ownedRelationship":[{"id":"ea9f50bb-2fc9-4934-b613-79a4bdf9ab0f","eClass":"sysml:Membership","data":{"elementId":"8f01928c-b0c1-4056-bfbb-64ca3335b251","memberElement":"15ccc1e3-16b9-4ddf-9959-43141f32d48e"}}]}}]}}],"direction":"in"}}]}},{"id":"7b4f5c1d-fbcd-4074-89bc-6928aecb206f","eClass":"sysml:ParameterMembership","data":{"elementId":"5ac072c4-4955-4803-b651-4d14c001f97e","ownedRelatedElement":[{"id":"41a595a8-9dfb-4542-936b-ba98891a4ea9","eClass":"sysml:Feature","data":{"elementId":"6f135276-6932-4db1-bd9c-126cb8475498","ownedRelationship":[{"id":"fb8829dc-0d44-4004-b7c6-2d617de26808","eClass":"sysml:FeatureValue","data":{"elementId":"73c691d9-1ad0-4357-92ae-8e02e690f5ef","ownedRelatedElement":[{"id":"15f2308f-9b14-4c40-b6a9-d496987dfc8b","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"45435f31-674c-40aa-ad49-ce170852a12b","ownedRelationship":[{"id":"78ed96c8-cc8b-488d-8970-524f4a8bd5be","eClass":"sysml:Membership","data":{"elementId":"5229ec54-8bd3-49e2-8221-a5f8a76142cc","memberElement":"89b903a2-10c2-42a7-8307-b8bc9a4ba76a"}}]}}]}}],"direction":"in"}}]}}],"operator":"<="}}]}}],"isComposite":true}}]}}]}}]}},{"id":"2c772a21-cd4b-472b-b9cb-2d291b86eff3","eClass":"sysml:OwningMembership","data":{"elementId":"2fcf9e17-3424-4677-b324-314c82774361","ownedRelatedElement":[{"id":"3f2124ff-5c56-486b-a296-b1c3476e5180","eClass":"sysml:PartDefinition","data":{"declaredName":"Sensor","elementId":"024ec40b-3a5b-46df-add7-4bcbec5523c7","ownedRelationship":[{"id":"560c46bc-4057-4b9e-8e5a-49e9e4109d05","eClass":"sysml:FeatureMembership","data":{"elementId":"7c0bc2eb-1488-4dd8-8ab9-afadef79a367","ownedRelatedElement":[{"id":"94c0a654-c5dc-4579-a655-89edda340104","eClass":"sysml:AttributeUsage","data":{"declaredName":"label","elementId":"46235c7e-3f38-4dfa-a061-fa5662c9ac1e","ownedRelationship":[{"id":"1758c3f6-d099-452d-8c1b-b375d51491fc","eClass":"sysml:FeatureValue","data":{"elementId":"4d70b01b-05c6-4646-b564-2035f4bde5be","ownedRelatedElement":[{"id":"ec30e68f-b6c2-4ab8-af03-9fea9ceaf2e3","eClass":"sysml:LiteralString","data":{"elementId":"1d905119-2ad9-41e5-8896-7c0d3f166e2b","value":"unnamed"}}]}},{"id":"6fd57c7e-3d50-49b3-94b7-133020804fc4","eClass":"sysml:FeatureTyping","data":{"elementId":"2a1b98bf-116f-44b6-a5cb-8aff68c8e1a8","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#76028d3d-69a4-5e12-9002-ce403e0244bd","typedFeature":"94c0a654-c5dc-4579-a655-89edda340104"}}],"isComposite":true}}]}},{"id":"ea09c1cf-a6fb-41a4-9a32-07f9a59d6ef4","eClass":"sysml:FeatureMembership","data":{"elementId":"c23a7f58-2c6b-45c3-bc49-1944baf5face","ownedRelatedElement":[{"id":"edc4f7bc-7d24-498e-8e76-ae1743668254","eClass":"sysml:AttributeUsage","data":{"declaredName":"enabled","elementId":"2aa70e33-7074-4c1d-8ef7-af6874857071","ownedRelationship":[{"id":"8cd41188-d199-4c49-ad23-10e366344c35","eClass":"sysml:FeatureValue","data":{"elementId":"7ea1ae0f-9769-4a93-8523-1b544171ffb4","ownedRelatedElement":[{"id":"5669db5e-ec61-40ca-9a52-8979126921d0","eClass":"sysml:LiteralBoolean","data":{"elementId":"14630a78-98b4-4a46-ac40-c8a49a7b7390","value":true}}]}},{"id":"2afde92b-9d7f-4e1c-ab29-bc9bd2967a82","eClass":"sysml:FeatureTyping","data":{"elementId":"0daf88b8-e462-482b-8220-4f19e3ce8380","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#d1e9242d-b2e3-5270-bf69-4f4fb0447193","typedFeature":"edc4f7bc-7d24-498e-8e76-ae1743668254"}}],"isComposite":true}}]}},{"id":"18f6bc41-fa40-4f81-b741-d601365a37a0","eClass":"sysml:FeatureMembership","data":{"elementId":"2b9522eb-72fb-4e5b-aed4-99209a770f92","ownedRelatedElement":[{"id":"b922024b-5587-4be6-9924-6afa52ce8bf2","eClass":"sysml:AttributeUsage","data":{"declaredName":"samplingRate","elementId":"ddec4b3c-42e0-4d8e-bd99-304def079d94","ownedRelationship":[{"id":"985de5ed-784f-465e-805b-6b7d88b3bddc","eClass":"sysml:FeatureValue","data":{"elementId":"5bb61755-a54e-4ea0-9ec7-e11adf867185","ownedRelatedElement":[{"id":"6d57d690-633a-4018-a0f6-5e732bc16897","eClass":"sysml:LiteralRational","data":{"elementId":"e5ef69e1-5daf-4dae-b0ed-1e7903b3d079","value":100.0}}]}},{"id":"0929e1d2-a563-4e37-befc-02767a3165ea","eClass":"sysml:FeatureTyping","data":{"elementId":"17f59ceb-baef-48a4-b67b-934ed58da6bf","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"b922024b-5587-4be6-9924-6afa52ce8bf2"}}],"isComposite":true}}]}},{"id":"48904b34-6744-4d90-8ea3-f8388f930416","eClass":"sysml:FeatureMembership","data":{"elementId":"a7691d9d-0037-4942-8a83-92cc93f14d7d","ownedRelatedElement":[{"id":"2f50920f-47d5-4508-a943-33f9a2d56a23","eClass":"sysml:AttributeUsage","data":{"declaredName":"channelCount","elementId":"4c2b5049-e9d0-4573-8fb7-032cbdcf8fdb","ownedRelationship":[{"id":"14910b2a-ff6e-4f7e-bb13-7e0979c49eca","eClass":"sysml:FeatureValue","data":{"elementId":"2d3e55d0-a26b-4a6b-ac99-f049047ada35","ownedRelatedElement":[{"id":"36055b10-a46d-42c9-95c2-901d02b7490a","eClass":"sysml:LiteralInteger","data":{"elementId":"4525ba90-45e6-4bb4-9a44-a2b3766560e8","value":4}}]}},{"id":"ffed96db-63ef-4e40-b41d-080f7dc68fef","eClass":"sysml:FeatureTyping","data":{"elementId":"c0034730-d919-4a37-ac87-9fc378662a0b","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#f2350199-2ab1-5258-8514-58812ef25dc6","typedFeature":"2f50920f-47d5-4508-a943-33f9a2d56a23"}}],"isComposite":true}}]}},{"id":"6292da99-b47c-48f9-8d1b-9115b1b0c4e9","eClass":"sysml:FeatureMembership","data":{"elementId":"c7efeeab-923d-42bc-9d62-8bf61c2ab2d7","ownedRelatedElement":[{"id":"6b9a0d65-eff4-4d98-8b25-d6e24d0486e9","eClass":"sysml:AttributeUsage","data":{"declaredName":"currentValue","elementId":"855d423f-5dbb-4042-b98c-5a34602b1d0e","ownedRelationship":[{"id":"d9b556a4-b6eb-4e20-90f6-e63908599859","eClass":"sysml:FeatureValue","data":{"elementId":"25fc714d-8228-45f3-98c1-d154cf129985","ownedRelatedElement":[{"id":"ba6195b6-3db6-410a-bf38-a22204c4263e","eClass":"sysml:LiteralRational","data":{"elementId":"31e7ced7-a849-497a-8bea-9e58b3969239"}}],"isInitial":true}},{"id":"0d5731f8-b287-4a2d-854b-32292f6082e2","eClass":"sysml:FeatureTyping","data":{"elementId":"9bb1f845-d859-45d9-b375-8749b0e46272","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"6b9a0d65-eff4-4d98-8b25-d6e24d0486e9"}}],"isComposite":true}}]}},{"id":"47e0493d-48fd-4a90-977b-f159c2e2dafe","eClass":"sysml:FeatureMembership","data":{"elementId":"7171788a-abcf-4895-8770-192412a650a6","ownedRelatedElement":[{"id":"4d2f8465-d748-4b68-a5a8-d61c03031bd3","eClass":"sysml:AttributeUsage","data":{"declaredName":"errorCount","elementId":"bfc48daf-6923-48f1-9d09-596579985dab","ownedRelationship":[{"id":"c0caf7ee-94b3-44f0-a03b-13c5bfa2f37a","eClass":"sysml:FeatureValue","data":{"elementId":"99cec056-4306-4921-805d-140ecc9ee5de","ownedRelatedElement":[{"id":"0e18cf3c-0d0f-4863-b4e2-407a9fcdad8c","eClass":"sysml:LiteralInteger","data":{"elementId":"cb488158-7555-424f-a37d-1c290aa3cee5"}}],"isInitial":true}},{"id":"96a56407-061b-4f40-81f5-4d8f5e211b56","eClass":"sysml:FeatureTyping","data":{"elementId":"db1c53ba-8888-4645-bbd1-4624a258a238","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#f2350199-2ab1-5258-8514-58812ef25dc6","typedFeature":"4d2f8465-d748-4b68-a5a8-d61c03031bd3"}}],"isComposite":true}}]}},{"id":"2b4f3de5-32fd-4859-9fe6-5689e017c120","eClass":"sysml:FeatureMembership","data":{"elementId":"24b2ddc3-2007-4fcd-a4b7-943e1bc43948","ownedRelatedElement":[{"id":"3f6b7e75-5039-485c-9fbc-776b772cd746","eClass":"sysml:AttributeUsage","data":{"declaredName":"active","elementId":"251cc6ef-d2c6-4893-941c-076709500c76","ownedRelationship":[{"id":"2ba77e81-8da1-4fdc-ba91-163988a8862a","eClass":"sysml:FeatureValue","data":{"elementId":"457e68ff-f795-4dd9-8643-2dd851d2387a","ownedRelatedElement":[{"id":"0e79ab60-92de-43dc-8aed-85bd61eaa2b7","eClass":"sysml:LiteralBoolean","data":{"elementId":"79052415-268e-41f4-8cf6-ac6484067e91"}}],"isInitial":true}},{"id":"bcd180b6-ecd8-47fc-8f28-08095cd9e23f","eClass":"sysml:FeatureTyping","data":{"elementId":"09b93323-05a1-429c-9dac-dc2672085d02","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#d1e9242d-b2e3-5270-bf69-4f4fb0447193","typedFeature":"3f6b7e75-5039-485c-9fbc-776b772cd746"}}],"isComposite":true}}]}}]}}]}},{"id":"e2cdac2f-1e6c-4a26-b0e9-75e47527ca5d","eClass":"sysml:OwningMembership","data":{"elementId":"c7c6f639-a46a-4360-bc06-4e25c313d91f","ownedRelatedElement":[{"id":"3c20f555-6ccd-448e-a15c-4c249ea54c78","eClass":"sysml:RequirementDefinition","data":{"declaredName":"SensorOperability","elementId":"03e626cf-73bb-4599-995d-b9b3051918cd","ownedRelationship":[{"id":"7be3ea0a-eff4-4a70-86b8-2b1f1d41fa7b","eClass":"sysml:SubjectMembership","data":{"elementId":"a1c897fc-f940-44ff-85b6-39795ec92fc9","ownedRelatedElement":[{"id":"26b28267-29e2-4668-bfb7-eea661577cb7","eClass":"sysml:ReferenceUsage","data":{"declaredName":"s","elementId":"846167d5-277a-4806-a2e6-4ce634cc69ae","ownedRelationship":[{"id":"472f5c5c-3914-4917-87c4-a4384613c261","eClass":"sysml:FeatureTyping","data":{"elementId":"86d37649-f240-4bce-9818-7461c477e982","type":"3f2124ff-5c56-486b-a296-b1c3476e5180","typedFeature":"26b28267-29e2-4668-bfb7-eea661577cb7"}}],"direction":"in","isComposite":true}}]}},{"id":"22084c71-6181-41af-8056-883abc445b79","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"1a9e4a22-4232-483f-8b6b-99eb80f74579","ownedRelatedElement":[{"id":"89b26ac6-c120-44c0-a9e2-437e2f28ddc1","eClass":"sysml:ConstraintUsage","data":{"declaredName":"environmentalPrecondition","elementId":"95a70f5f-b564-4922-8bce-0a84230d07ac","ownedRelationship":[{"id":"03243978-41a3-44ca-94e3-70ffcbb4f56e","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"b6091299-b78a-4249-a9b4-b38de007165f","ownedRelatedElement":[{"id":"f97e3c19-2b5b-4daf-9f0d-017fe9f962f7","eClass":"sysml:OperatorExpression","data":{"elementId":"f33ab7c6-5006-4de8-b53a-62dc5fc24325","ownedRelationship":[{"id":"b5562f52-e2ce-4d03-8ed3-7b95ea472194","eClass":"sysml:ParameterMembership","data":{"elementId":"c44bf22b-24cd-42bd-aadf-e857271935b2","ownedRelatedElement":[{"id":"9fd3acc6-0c9e-4d89-ab4a-d58627712cb3","eClass":"sysml:Feature","data":{"elementId":"f7c53edb-8c09-4124-ae5d-96c5238f5d2e","ownedRelationship":[{"id":"3cf505dd-61d8-43da-b52e-84d73e9348ca","eClass":"sysml:FeatureValue","data":{"elementId":"ca481874-8b4a-482b-9f84-078ab69c83b9","ownedRelatedElement":[{"id":"f23fd077-7725-426f-924a-85a00a0ea779","eClass":"sysml:OperatorExpression","data":{"elementId":"bf6bd6fe-047b-456d-ba8c-92f8ffb5deba","ownedRelationship":[{"id":"e5099d7a-6a81-4061-b507-5ef72d7933f0","eClass":"sysml:ParameterMembership","data":{"elementId":"51cd9462-617c-4179-a552-eea9e8c766a4","ownedRelatedElement":[{"id":"17daf4fb-22ee-4a70-a84f-63a09ccee784","eClass":"sysml:Feature","data":{"elementId":"e880c32a-6ed3-498a-aeb4-d32250bcc061","ownedRelationship":[{"id":"3a586119-f53f-40f1-9fae-4b2e29a6e57e","eClass":"sysml:FeatureValue","data":{"elementId":"f22c0876-8374-4eca-ad79-d1d7053c88a4","ownedRelatedElement":[{"id":"d31bb8ba-e377-4983-bfc0-ed58e89d54f8","eClass":"sysml:FeatureChainExpression","data":{"elementId":"fe4bac6d-b31c-4a49-97b8-75ac2b853ab9","ownedRelationship":[{"id":"35bd3dc6-5510-49ec-ae49-8b5d11f27578","eClass":"sysml:ParameterMembership","data":{"elementId":"3b28eb00-c108-491d-a39f-97dd584fc5b3","ownedRelatedElement":[{"id":"75251a12-c877-4043-8ea4-0c7953c073fc","eClass":"sysml:Feature","data":{"elementId":"8070b6d4-13e9-42e9-bd09-95f4f0168623","ownedRelationship":[{"id":"0bf3106a-9de8-492a-86ef-6c37d1a870ab","eClass":"sysml:FeatureValue","data":{"elementId":"d376ea9b-17ac-4eea-961b-cd0d59ab38fe","ownedRelatedElement":[{"id":"46af9d73-20cb-470a-9914-53f136e2eb36","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"e1665ec4-a3c1-417a-ba5b-6778561f9fc0","ownedRelationship":[{"id":"ddeb93ce-38fa-4474-895f-e58832028e14","eClass":"sysml:Membership","data":{"elementId":"80218b82-c542-40a4-a20c-c413faa628da","memberElement":"26b28267-29e2-4668-bfb7-eea661577cb7"}}]}}]}}],"direction":"in"}}]}},{"id":"0adca218-7d4b-4068-8ab5-7f324f83dc5e","eClass":"sysml:Membership","data":{"elementId":"087cc752-6838-47d8-aa38-d88990a35a70","memberElement":"edc4f7bc-7d24-498e-8e76-ae1743668254"}}]}}]}}],"direction":"in"}}]}},{"id":"2d487599-d100-47a2-9f9d-60f4e3c7effd","eClass":"sysml:ParameterMembership","data":{"elementId":"43fecf2d-ee36-4680-9295-cf4cdf6e5e82","ownedRelatedElement":[{"id":"5c057fe8-07d0-4a61-8aa1-155a9870b97a","eClass":"sysml:Feature","data":{"elementId":"d8e6c10f-3ced-40a5-becd-286d7a1e5e2d","ownedRelationship":[{"id":"c7926f63-0e5a-4abf-9f05-d3f7ede1df40","eClass":"sysml:FeatureValue","data":{"elementId":"a1742093-8ec5-4438-a7a3-ffea0821efd3","ownedRelatedElement":[{"id":"daaecec2-38f8-470f-92be-4e76742d5bdc","eClass":"sysml:LiteralBoolean","data":{"elementId":"3c8b2228-7d6d-45ba-bfd1-3a80983c13fb","value":true}}]}}],"direction":"in"}}]}}],"operator":"=="}}]}}],"direction":"in"}}]}},{"id":"f9ec458e-862b-45a2-b4f7-ae808551b406","eClass":"sysml:ParameterMembership","data":{"elementId":"67d15eef-d95a-4262-a3d7-ac8c73f37966","ownedRelatedElement":[{"id":"aac0f2a4-f51a-41b0-ab00-d77e005ba716","eClass":"sysml:Feature","data":{"elementId":"e0346753-1dfc-4ec3-b0aa-d670b6e6b5ee","ownedRelationship":[{"id":"a635c6f5-4992-4560-8df6-4cf94e9678fe","eClass":"sysml:FeatureValue","data":{"elementId":"825de020-d7cf-4539-a128-36e9ef1ccba4","ownedRelatedElement":[{"id":"a3087e8f-fc12-4e29-b108-9c9debcedd1e","eClass":"sysml:OperatorExpression","data":{"elementId":"093dc3ba-ae58-4d39-94be-e04dd6d55396","ownedRelationship":[{"id":"51f5bb92-e99e-4a13-89eb-414ce7331c77","eClass":"sysml:ParameterMembership","data":{"elementId":"b9d31569-e464-4f5c-8deb-6bf749306271","ownedRelatedElement":[{"id":"e91251cd-8d24-4c11-b777-550b6147ae63","eClass":"sysml:Feature","data":{"elementId":"606a4d32-c5ac-4276-9015-45c24fd27779","ownedRelationship":[{"id":"afeff4b6-1d9a-441d-a692-a527f60625f0","eClass":"sysml:FeatureValue","data":{"elementId":"32569bac-9960-4873-8a55-50899cbfb2fb","ownedRelatedElement":[{"id":"1800b695-f852-4f75-b124-7086083ef9a9","eClass":"sysml:FeatureChainExpression","data":{"elementId":"42680337-a1c6-4445-a6e6-571aab4ea27e","ownedRelationship":[{"id":"341cbae7-5abe-4fb9-80f4-a647450fcf31","eClass":"sysml:ParameterMembership","data":{"elementId":"cded185e-f09f-4c95-9802-b25d02fa97f7","ownedRelatedElement":[{"id":"36c40f15-53d9-4da0-bba9-7097c2b14972","eClass":"sysml:Feature","data":{"elementId":"b20c603d-d30e-41a2-a81f-d27c5603a17e","ownedRelationship":[{"id":"02a48840-7c5a-47da-b726-76152b533eb7","eClass":"sysml:FeatureValue","data":{"elementId":"fbe218e1-dcca-4266-8df8-ef8d20ff4d98","ownedRelatedElement":[{"id":"1acc5b62-4d4a-40d7-b92c-bbcc5acea29e","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"2b9347c6-9600-422b-bd62-f504a00c1690","ownedRelationship":[{"id":"d89b10dd-9b95-49ec-8acb-41a135c3359d","eClass":"sysml:Membership","data":{"elementId":"95a02965-4d2b-4766-a29b-bcc42f863d6d","memberElement":"26b28267-29e2-4668-bfb7-eea661577cb7"}}]}}]}}],"direction":"in"}}]}},{"id":"ae0a3065-f5ec-4168-9be2-9816bb3a84a0","eClass":"sysml:Membership","data":{"elementId":"8919a2b6-9784-453e-abf2-940fb136e4d2","memberElement":"b922024b-5587-4be6-9924-6afa52ce8bf2"}}]}}]}}],"direction":"in"}}]}},{"id":"6720a737-26ee-4583-865a-66f1d30e2119","eClass":"sysml:ParameterMembership","data":{"elementId":"9e51fe6d-89dd-4911-854a-825f35355b80","ownedRelatedElement":[{"id":"001b01d8-02c0-4f02-851f-b9baeb0465a6","eClass":"sysml:Feature","data":{"elementId":"df7244c0-cfea-4b87-b5e6-3131be5b7abf","ownedRelationship":[{"id":"c1f7d964-fd30-4ebd-b0a5-dc0937c013fe","eClass":"sysml:FeatureValue","data":{"elementId":"782a1e9c-878d-447d-a0d0-6a865c708f8e","ownedRelatedElement":[{"id":"8ebe7473-9ea1-4965-8a43-a4c1b7ff3ead","eClass":"sysml:LiteralRational","data":{"elementId":"7f088100-1afb-402f-9cba-5976381966ef"}}]}}],"direction":"in"}}]}}],"operator":">"}}]}}],"direction":"in"}}]}}],"operator":"&"}}]}}],"isComposite":true}}]}},{"id":"fd89f7d5-d5e4-4090-b71f-7b7edc56f820","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"9132678c-8721-4d13-9c1b-6421f10b9fa7","ownedRelatedElement":[{"id":"43818255-7ffb-4ef9-ad34-603c623a0a44","eClass":"sysml:ConstraintUsage","data":{"declaredName":"noInitialErrors","elementId":"ec6684fe-fe49-4b1d-8597-8db46655170a","ownedRelationship":[{"id":"305f2777-fc7c-47cb-b077-95c960fcaef7","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"4e0495c2-a1b1-4f0f-b505-b77a1e09fe91","ownedRelatedElement":[{"id":"674bd9db-8e8f-40a5-afbb-42602be3f4b1","eClass":"sysml:OperatorExpression","data":{"elementId":"74a13c5e-2b68-4aac-ac54-58940cac49a8","ownedRelationship":[{"id":"0fd5a82f-02b2-453f-ae3d-404b44d923a2","eClass":"sysml:ParameterMembership","data":{"elementId":"75dfe7c0-47f2-458f-bc4d-61692f7039b6","ownedRelatedElement":[{"id":"9b0f0b70-d567-42c4-b3de-042256e9f877","eClass":"sysml:Feature","data":{"elementId":"22528542-1576-44a2-85c4-9a278e5af324","ownedRelationship":[{"id":"ffe8010f-e97a-43f4-8f76-634d2d3a4a1c","eClass":"sysml:FeatureValue","data":{"elementId":"2a9edcb9-a96c-432b-87a9-af56ce864c5c","ownedRelatedElement":[{"id":"5b482eaf-c916-4953-8a82-8c6c9f36481c","eClass":"sysml:FeatureChainExpression","data":{"elementId":"fa6b2042-eef7-408c-b5d3-8a5094eec9e3","ownedRelationship":[{"id":"322ad996-7446-413a-8b00-fe0a39e53dea","eClass":"sysml:ParameterMembership","data":{"elementId":"6ec90405-8ae3-43b4-b295-1b0af8e3cdfe","ownedRelatedElement":[{"id":"b5b22abf-5a4f-4d1b-940f-f9ffd76cc274","eClass":"sysml:Feature","data":{"elementId":"2fc69bcb-4408-4e6b-8d37-6e2509a0f707","ownedRelationship":[{"id":"b6257642-230e-4847-815e-2dc125933313","eClass":"sysml:FeatureValue","data":{"elementId":"e58838f0-f500-4ce3-9b28-1cb05ab6c0ab","ownedRelatedElement":[{"id":"ab026b01-c127-4f2e-af15-6232d7453a0b","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"426b9f08-b36d-424d-ae1c-72e19f089688","ownedRelationship":[{"id":"ee54e3cf-6459-441c-b58f-d31278a72ecb","eClass":"sysml:Membership","data":{"elementId":"595198d2-7c69-40b2-9606-0f87f250e701","memberElement":"26b28267-29e2-4668-bfb7-eea661577cb7"}}]}}]}}],"direction":"in"}}]}},{"id":"5d654f07-ff8c-411a-9339-290f4b364f52","eClass":"sysml:Membership","data":{"elementId":"2f356da4-c6d7-4281-aed5-5074486d209f","memberElement":"4d2f8465-d748-4b68-a5a8-d61c03031bd3"}}]}}]}}],"direction":"in"}}]}},{"id":"6c32c3c0-8ecf-4bd8-92b2-0637b65689bf","eClass":"sysml:ParameterMembership","data":{"elementId":"af68c65d-a92e-44ec-b189-ec2f6aefaae2","ownedRelatedElement":[{"id":"d4cbf635-a286-4a5a-a240-46e56902b2e7","eClass":"sysml:Feature","data":{"elementId":"d7de254e-2bc4-4857-bc91-eb2488f9fa82","ownedRelationship":[{"id":"6209d9f9-68a3-4b82-a6b9-751127988355","eClass":"sysml:FeatureValue","data":{"elementId":"d8c0c7be-09d7-4dad-8ccb-7dd06b358f00","ownedRelatedElement":[{"id":"7a0f2b0f-3843-41c5-b88f-90cee6e079b9","eClass":"sysml:LiteralInteger","data":{"elementId":"1c8e5633-07ae-47b2-852e-620bdfc728cc"}}]}}],"direction":"in"}}]}}],"operator":"=="}}]}}],"isComposite":true}}]}},{"id":"1015f3d7-efa6-4de1-b045-446b4186c2f2","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"7c3fdec2-1586-426d-903b-10d1fa9b388b","ownedRelatedElement":[{"id":"744de1e8-a545-4d5f-9c22-0b5a077f6d16","eClass":"sysml:ConstraintUsage","data":{"declaredName":"samplingInBounds","elementId":"710c2d73-2c5e-4aaf-a2f2-0e0cedb31d52","ownedRelationship":[{"id":"0d5711b7-29a8-4e88-9c96-6b98678b32a9","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"8c4d2994-52bd-4fec-9528-2f14b2e3a550","ownedRelatedElement":[{"id":"fb8ef886-b55a-43d0-bf93-0a67b2426fb9","eClass":"sysml:OperatorExpression","data":{"elementId":"0a64137e-061e-47e0-a371-ee0d95490567","ownedRelationship":[{"id":"1eeb9c5e-cc00-4f7d-ab0b-835faaddbad9","eClass":"sysml:ParameterMembership","data":{"elementId":"82a132ad-0499-45ed-ba57-f80116752617","ownedRelatedElement":[{"id":"1ce0a55e-ef7d-46c5-801c-639df3264258","eClass":"sysml:Feature","data":{"elementId":"5e66ffe4-195f-4c13-afee-ed4f0164cbf3","ownedRelationship":[{"id":"7e2410ff-1fc4-40bc-8a1f-f67cd3380704","eClass":"sysml:FeatureValue","data":{"elementId":"68322213-a33f-4ac1-a1e9-e5bcfa153489","ownedRelatedElement":[{"id":"4dc3b7ac-ed2b-4b07-ac88-d1b0b0174946","eClass":"sysml:OperatorExpression","data":{"elementId":"cc836d15-6462-49f6-90d6-3e1fe85d57f5","ownedRelationship":[{"id":"ab468039-0014-4e6f-b1a4-d8cd92370649","eClass":"sysml:ParameterMembership","data":{"elementId":"3478fa32-bda9-48ae-b44d-4d8e1a8b745b","ownedRelatedElement":[{"id":"dc1b31db-45df-48c3-bf0c-0925a5ce11f9","eClass":"sysml:Feature","data":{"elementId":"4dbe01b8-e406-48f9-878e-b1925d0b94bf","ownedRelationship":[{"id":"178fe70f-8113-4e4e-aaa0-e23253640576","eClass":"sysml:FeatureValue","data":{"elementId":"86f7f3a3-d8e8-415b-a284-a9e744b42abe","ownedRelatedElement":[{"id":"56645ba2-6aef-4a6a-a66e-8191b5c87dbf","eClass":"sysml:FeatureChainExpression","data":{"elementId":"6514848e-8123-44b8-9acc-7f9d9db8cb8b","ownedRelationship":[{"id":"ec6dda59-1e10-4ed6-ad3d-7114fd5e09cd","eClass":"sysml:ParameterMembership","data":{"elementId":"b17edd31-837e-4a66-a9f9-d92e0da08a62","ownedRelatedElement":[{"id":"17f67cfc-824a-4aeb-bd36-999ec420d783","eClass":"sysml:Feature","data":{"elementId":"d83c9b8b-ad1e-4add-b43c-aebddbd54243","ownedRelationship":[{"id":"855f40bf-bca7-4599-aef6-e58ae1249a5b","eClass":"sysml:FeatureValue","data":{"elementId":"97b6dc0b-a32e-4d9d-b40b-3a37d0143b19","ownedRelatedElement":[{"id":"13182f23-36f8-4335-8756-fe068510f668","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"1c0f3f4e-7e52-417a-8e2c-28958a350b43","ownedRelationship":[{"id":"10ec6e74-d315-470d-838c-ee910f4a56a5","eClass":"sysml:Membership","data":{"elementId":"e312bc35-8594-4a20-8ff7-c101dc2090fd","memberElement":"26b28267-29e2-4668-bfb7-eea661577cb7"}}]}}]}}],"direction":"in"}}]}},{"id":"fcbae642-5dfa-414e-91ff-2106f907a48e","eClass":"sysml:Membership","data":{"elementId":"7f68ad69-159f-4675-9f02-35da9cba4740","memberElement":"b922024b-5587-4be6-9924-6afa52ce8bf2"}}]}}]}}],"direction":"in"}}]}},{"id":"d0e9d92c-225b-462e-a99b-394b2895b660","eClass":"sysml:ParameterMembership","data":{"elementId":"5c481b3a-ec1b-4fc1-9f9f-900c59712400","ownedRelatedElement":[{"id":"9df55992-f8c4-4ebc-9589-486477e856de","eClass":"sysml:Feature","data":{"elementId":"77f13915-1767-44f3-9711-cc035cf30882","ownedRelationship":[{"id":"8252fef3-a39c-4990-842a-495495cbffcc","eClass":"sysml:FeatureValue","data":{"elementId":"d74880a1-050c-4b54-a015-c38e1b1451c9","ownedRelatedElement":[{"id":"40f6c556-0ae7-47a9-b845-6217b18a624e","eClass":"sysml:LiteralRational","data":{"elementId":"1983f1ed-8ecf-4faa-9f62-3c12e2af44cf","value":1.0}}]}}],"direction":"in"}}]}}],"operator":">="}}]}}],"direction":"in"}}]}},{"id":"02abeed7-427a-46aa-8a22-5b9cbb4ece78","eClass":"sysml:ParameterMembership","data":{"elementId":"a0bb41de-1d00-4001-8d5f-595698159369","ownedRelatedElement":[{"id":"3e5d55cb-dcb1-4fc5-9aa4-2d11e2a737e1","eClass":"sysml:Feature","data":{"elementId":"c663de85-1135-4e49-b3a9-e08ad8f18d46","ownedRelationship":[{"id":"3e84f11c-cb4a-4e34-bfb4-60ce73072d4f","eClass":"sysml:FeatureValue","data":{"elementId":"90242b82-6b90-48eb-9e79-7ae21c2405b3","ownedRelatedElement":[{"id":"0dbf27e7-d9a3-4dde-b801-c8ba92ecaf67","eClass":"sysml:OperatorExpression","data":{"elementId":"9f79e4e0-5f49-44e4-9336-bd6c6600deb3","ownedRelationship":[{"id":"b4015705-c1e1-4630-9892-ca94207213a3","eClass":"sysml:ParameterMembership","data":{"elementId":"8b7a8b36-4faa-4241-9af4-7119e2ea978f","ownedRelatedElement":[{"id":"863bc422-95cb-4321-adaf-4e7c202d3ac0","eClass":"sysml:Feature","data":{"elementId":"605bf947-1c79-4920-8f35-bb22cc84d3e8","ownedRelationship":[{"id":"b8ff86a1-8074-4ceb-9943-234263d0ab0a","eClass":"sysml:FeatureValue","data":{"elementId":"45f79d0d-eb20-449e-b0a1-0578ac65ee3a","ownedRelatedElement":[{"id":"65527d7d-d7b5-4a89-91b4-7ef154a4c8c5","eClass":"sysml:FeatureChainExpression","data":{"elementId":"6aef4085-719f-47b5-8d52-3aacdcb46cff","ownedRelationship":[{"id":"f97d2edd-e85b-49b3-ae3d-ed59c67efc20","eClass":"sysml:ParameterMembership","data":{"elementId":"4740471e-5432-4b9f-a8c4-7fcb0d3bab9e","ownedRelatedElement":[{"id":"ac4bf947-176f-4872-9539-9d558cb9f4d4","eClass":"sysml:Feature","data":{"elementId":"d32ac236-1b32-483a-bbc5-0363583dfaab","ownedRelationship":[{"id":"c1a41a4b-2a5f-4503-8433-c6d57a309dbd","eClass":"sysml:FeatureValue","data":{"elementId":"16ce4186-e717-40bc-9b42-b7abe98c5722","ownedRelatedElement":[{"id":"0332b5da-304d-4b3b-a128-be7ac0e4db43","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"2e1db3c1-d2ab-4651-a8b5-91bf0e9efafc","ownedRelationship":[{"id":"bf180bff-3dac-451d-ac17-3f1f95a44212","eClass":"sysml:Membership","data":{"elementId":"c2dec826-b8ba-4744-9c32-ba0a98d8a050","memberElement":"26b28267-29e2-4668-bfb7-eea661577cb7"}}]}}]}}],"direction":"in"}}]}},{"id":"104e5faf-4077-4efd-8cf2-fff17b6ac41f","eClass":"sysml:Membership","data":{"elementId":"4f5fe8c1-3da6-4e47-bf81-c1b89cf293c9","memberElement":"b922024b-5587-4be6-9924-6afa52ce8bf2"}}]}}]}}],"direction":"in"}}]}},{"id":"b14481fe-2a63-4932-98e3-54a57dad9118","eClass":"sysml:ParameterMembership","data":{"elementId":"9e944981-882f-4340-a990-ef9b0d040bbb","ownedRelatedElement":[{"id":"7aab7c53-21c2-4d79-8216-4f01191a9f34","eClass":"sysml:Feature","data":{"elementId":"fb8fe825-cee3-48dd-8603-bac9fc6b67a5","ownedRelationship":[{"id":"86f1647e-66a1-4482-ba36-485f4663f643","eClass":"sysml:FeatureValue","data":{"elementId":"14cb76eb-886f-4221-9250-bcc0b8b6c81a","ownedRelatedElement":[{"id":"4425218f-1718-455d-9e86-81a82c6b1f3f","eClass":"sysml:LiteralRational","data":{"elementId":"e6bfe9d8-01b1-4b40-8645-182732d2c796","value":10000.0}}]}}],"direction":"in"}}]}}],"operator":"<="}}]}}],"direction":"in"}}]}}],"operator":"&"}}]}}],"isComposite":true}}],"kind":"requirement"}},{"id":"13b6da3e-7222-4755-a0db-73ab0e7cef43","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"74e11b1f-78ff-4a64-98d2-8d9f206f5345","ownedRelatedElement":[{"id":"5430a222-7a32-41d9-bff7-f3437bfb2541","eClass":"sysml:ConstraintUsage","data":{"declaredName":"stableOutput","elementId":"78954a81-8ed8-4abb-94e5-260396996d8c","ownedRelationship":[{"id":"9bdf03b3-0eef-46b2-b407-ea5942872033","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"7eeee652-e8b0-4b38-be9b-cbbc02d9d2b6","ownedRelatedElement":[{"id":"e2a16041-e92b-4352-a11f-e934e3f77244","eClass":"sysml:OperatorExpression","data":{"elementId":"162b90b8-ac96-4326-a8fc-e541d38f7ea5","ownedRelationship":[{"id":"2bb34634-e23e-4e36-af6d-4a4135a1da3e","eClass":"sysml:ParameterMembership","data":{"elementId":"50ee9282-e9d8-4055-b96c-e14a15715951","ownedRelatedElement":[{"id":"0d02bb77-2368-4995-b01d-f9767c18f91c","eClass":"sysml:Feature","data":{"elementId":"5b6a39a6-ba11-4b57-9e29-37d4c5d062ee","ownedRelationship":[{"id":"50730cce-15f8-41ec-b73f-373406c56ad3","eClass":"sysml:FeatureValue","data":{"elementId":"4e6818ae-48cc-48e2-b924-93006bded6d1","ownedRelatedElement":[{"id":"6457b597-51ee-43d3-b47a-d6185b962d52","eClass":"sysml:OperatorExpression","data":{"elementId":"1c19c360-b9a3-4c40-ac36-f4307bb173ec","ownedRelationship":[{"id":"87d2c594-9d82-4eee-afbd-da23103c7bce","eClass":"sysml:ParameterMembership","data":{"elementId":"4be4a607-9609-4c3d-ba9f-8a4ef5272115","ownedRelatedElement":[{"id":"c84d00e0-8a5d-420c-93d0-426f57ae9bff","eClass":"sysml:Feature","data":{"elementId":"ac7d8f05-80d1-42dc-a844-c29f0a3c8165","ownedRelationship":[{"id":"05c17c5f-82e5-47a2-92ec-60c73c48e28c","eClass":"sysml:FeatureValue","data":{"elementId":"46806968-3ec7-4de2-ba39-22a3da992209","ownedRelatedElement":[{"id":"ca61c2d5-59fd-42ea-903a-130686e54e4e","eClass":"sysml:FeatureChainExpression","data":{"elementId":"2f496fa4-27db-45bc-8866-6dab5894dc7e","ownedRelationship":[{"id":"f5ad2a2c-0e3e-4a42-8dae-b26d4acd689d","eClass":"sysml:ParameterMembership","data":{"elementId":"02caa747-a770-4bb4-8996-4dd84116ec63","ownedRelatedElement":[{"id":"c925544f-75ce-4ca2-8a5b-73ec1da5876e","eClass":"sysml:Feature","data":{"elementId":"97535996-cbed-448d-a316-57100f9c3f93","ownedRelationship":[{"id":"c2607860-5083-4aef-8014-135d7bb0bd5c","eClass":"sysml:FeatureValue","data":{"elementId":"e2228dbe-f53b-4aa8-b3ea-545291c3d6df","ownedRelatedElement":[{"id":"d5de3a0c-27c9-453b-b6c9-9b71a2badddc","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"08c644d4-4b03-4009-a5e5-3d38837a2c66","ownedRelationship":[{"id":"fb070d5a-358b-4d86-9271-98ecf17af4f9","eClass":"sysml:Membership","data":{"elementId":"4bc3df72-8616-469d-ad93-3937b185dc79","memberElement":"26b28267-29e2-4668-bfb7-eea661577cb7"}}]}}]}}],"direction":"in"}}]}},{"id":"f2886c92-7a40-44fd-b80a-fd9e66bf295d","eClass":"sysml:Membership","data":{"elementId":"bd803489-0cab-42a9-abdf-f3653adb4f63","memberElement":"6b9a0d65-eff4-4d98-8b25-d6e24d0486e9"}}]}}]}}],"direction":"in"}}]}},{"id":"592bb152-0628-4587-b6da-1babfb6a4899","eClass":"sysml:ParameterMembership","data":{"elementId":"76ddca74-30e9-4d15-ad76-fc1ddc2520b2","ownedRelatedElement":[{"id":"3ce88764-e833-46f1-90f3-bfe64ce8fd65","eClass":"sysml:Feature","data":{"elementId":"c70d7936-c85b-42c8-b0c1-ccf8f5d30c21","ownedRelationship":[{"id":"5c224c44-af1e-49f3-b965-210e58d2cf15","eClass":"sysml:FeatureValue","data":{"elementId":"0d6a2d4f-ae7c-4089-9272-85dff9c776e1","ownedRelatedElement":[{"id":"06924b07-5194-4335-ac55-11054257b115","eClass":"sysml:OperatorExpression","data":{"elementId":"aa88b9d7-0fca-4991-967d-cdfed6b3e2ea","ownedRelationship":[{"id":"4b5ef4d7-92e0-4402-bb6f-96f74483d2ca","eClass":"sysml:ParameterMembership","data":{"elementId":"d1595baa-e83d-44aa-8410-68705291932e","ownedRelatedElement":[{"id":"3c2c1075-8ff7-4d04-83ad-4e64990a60ea","eClass":"sysml:Feature","data":{"elementId":"caa645a9-d5a4-465a-bdf5-51c86fa9865f","ownedRelationship":[{"id":"28697868-fc54-4ed3-8027-1667d4eb550b","eClass":"sysml:FeatureValue","data":{"elementId":"0afe35e7-67d5-48f2-a0dc-5f8ba0c88941","ownedRelatedElement":[{"id":"dbb371b6-8314-403a-b45c-fcbea954bfaa","eClass":"sysml:LiteralRational","data":{"elementId":"49bfa9b9-2fec-44ec-bdaa-c1d1fa197d5c","value":1000.0}}]}}],"direction":"in"}}]}}],"operator":"-"}}]}}],"direction":"in"}}]}}],"operator":">="}}]}}],"direction":"in"}}]}},{"id":"9061073e-f8c8-4cc6-9064-303071dcfea0","eClass":"sysml:ParameterMembership","data":{"elementId":"2e0797de-4bab-4855-8c06-c56c54cf5735","ownedRelatedElement":[{"id":"0aadea47-f0cf-4ef7-9f2b-e61a0ee9cbd7","eClass":"sysml:Feature","data":{"elementId":"8449c67b-26a9-4ee8-a3a1-2e1adb96c980","ownedRelationship":[{"id":"afa68833-3809-4200-abc6-8ad5e6ae7657","eClass":"sysml:FeatureValue","data":{"elementId":"a086a6c5-8d87-437e-9b24-7305389bae35","ownedRelatedElement":[{"id":"aea8971f-abff-401f-b81c-96e82ded07ba","eClass":"sysml:OperatorExpression","data":{"elementId":"9ef4ebbe-09c8-4af7-88f3-713d66115c5c","ownedRelationship":[{"id":"6f815ed1-4211-4dc0-a79e-e0557d93c6e7","eClass":"sysml:ParameterMembership","data":{"elementId":"5b55cf36-a0bb-43d5-83a9-6fed193bb3c6","ownedRelatedElement":[{"id":"fc0e0ff2-97c1-4ee3-be70-96d783c300f3","eClass":"sysml:Feature","data":{"elementId":"4cc2d2c6-d8f9-4903-af9c-35d0732ba1ec","ownedRelationship":[{"id":"93fd7287-17c7-438f-8d4a-98d85c36ca0b","eClass":"sysml:FeatureValue","data":{"elementId":"8c006f9f-34c6-4f01-9238-3c8db61fff24","ownedRelatedElement":[{"id":"0147be8c-d4d1-4ce6-8cfd-c67be64a4ea5","eClass":"sysml:FeatureChainExpression","data":{"elementId":"e58e7aae-3384-4534-9b9e-ff145d305049","ownedRelationship":[{"id":"3e8ef9ad-5952-44e3-ba6f-1e2f0220e723","eClass":"sysml:ParameterMembership","data":{"elementId":"e31ece95-3cb1-481c-9c88-cef2a99755bd","ownedRelatedElement":[{"id":"7b911b07-3b0b-41f0-8228-a5b5904132ec","eClass":"sysml:Feature","data":{"elementId":"766277b4-ff73-477c-ace3-91e98f09afa4","ownedRelationship":[{"id":"b0669556-a14c-4c44-b187-e36f7ab247f2","eClass":"sysml:FeatureValue","data":{"elementId":"a08b25ec-f69a-406d-8ab6-fcf60b5a7c79","ownedRelatedElement":[{"id":"d40caf8d-5643-4874-8f1a-85454b0a6a96","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"3fb26596-7403-4534-a34e-c470eb196ba3","ownedRelationship":[{"id":"2d661746-ba83-4edf-9f05-ee4ae4d2d147","eClass":"sysml:Membership","data":{"elementId":"9386c8fd-497a-49f0-86cd-e705cce8e016","memberElement":"26b28267-29e2-4668-bfb7-eea661577cb7"}}]}}]}}],"direction":"in"}}]}},{"id":"e945de86-7654-4a0f-8f9f-419fb2539a70","eClass":"sysml:Membership","data":{"elementId":"df8e42c4-9d75-4807-8590-97a9f3e118df","memberElement":"6b9a0d65-eff4-4d98-8b25-d6e24d0486e9"}}]}}]}}],"direction":"in"}}]}},{"id":"d908598b-b102-45e2-8607-791d3785936c","eClass":"sysml:ParameterMembership","data":{"elementId":"94d09dbb-d0ff-40d7-b495-6ca152a8f689","ownedRelatedElement":[{"id":"09cf2f87-4b62-4b48-b431-ec3a9606162f","eClass":"sysml:Feature","data":{"elementId":"5b25b441-dbaa-471c-a034-3e28ef3328ea","ownedRelationship":[{"id":"c048582f-5c86-42bc-83f6-0fd41c208999","eClass":"sysml:FeatureValue","data":{"elementId":"772bfeb3-315a-45ac-97f4-b965ecab8d02","ownedRelatedElement":[{"id":"951b914d-f33b-4028-a731-98cbb9282f2f","eClass":"sysml:LiteralRational","data":{"elementId":"a9a07aae-af6e-400b-8ec6-6c307ef005f9","value":1000.0}}]}}],"direction":"in"}}]}}],"operator":"<="}}]}}],"direction":"in"}}]}}],"operator":"&"}}]}}],"isComposite":true}}],"kind":"requirement"}}]}}]}},{"id":"0f77318f-48df-4fd5-b4fe-c2470070695a","eClass":"sysml:OwningMembership","data":{"elementId":"3f6dda74-d5cc-4bc5-b728-79fd8dc7a929","ownedRelatedElement":[{"id":"2efcf5d4-4948-4b7d-b779-f3f694eb8165","eClass":"sysml:StateDefinition","data":{"declaredName":"ThermalControl","elementId":"040718ea-26b5-47d6-ba84-9f7c43fe2c07","ownedRelationship":[{"id":"6bd335ce-5089-4089-902f-917b1b6fed36","eClass":"sysml:StateSubactionMembership","data":{"elementId":"ded89b53-c1f0-4c68-8a56-d7283bc2b862","ownedRelatedElement":[{"id":"ac85c0b8-2d5a-491f-81dc-fb9b09b3ab31","eClass":"sysml:ActionUsage","data":{"elementId":"fd138068-384e-4be8-b885-ef3b218e006f","isComposite":true}}],"kind":"entry"}},{"id":"0345c054-7f57-45ec-8473-1261313b14d6","eClass":"sysml:FeatureMembership","data":{"elementId":"ea20286f-4020-4488-bfdd-f8b8c0293486","ownedRelatedElement":[{"id":"0a63014b-116e-455e-85f5-bc66c922d691","eClass":"sysml:TransitionUsage","data":{"elementId":"ddef46f5-05d7-411e-b826-3f1f3fea21ff","ownedRelationship":[{"id":"baa2a68d-e8f5-470c-b497-eda44fe94d59","eClass":"sysml:Membership","data":{"elementId":"0ea170b2-1597-4c55-b3e8-6cf8b7a9d204","memberElement":"ac85c0b8-2d5a-491f-81dc-fb9b09b3ab31"}},{"id":"0e6d29f8-3a63-48d1-af12-339e173ae602","eClass":"sysml:OwningMembership","data":{"elementId":"f5f627e0-538f-4fd9-b64b-a14966f22482","ownedRelatedElement":[{"id":"c59c0174-da0a-4972-a5ca-6743ab295651","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"f55e815c-45ae-4320-9753-a362b3c8e6e5","ownedRelationship":[{"id":"38f392ae-b052-4618-8c6d-8c01e635a521","eClass":"sysml:EndFeatureMembership","data":{"elementId":"8f15832d-752e-417c-a176-520904b7dfe1","ownedRelatedElement":[{"id":"a2b00582-c35c-41cf-86c2-49ab6a853807","eClass":"sysml:ReferenceUsage","data":{"elementId":"293900dd-1f99-49d4-8638-8c541b96ed6b","isEnd":true}}]}},{"id":"bf9eb61a-6c2d-4a2e-af99-6aec971b7e95","eClass":"sysml:EndFeatureMembership","data":{"elementId":"067cb51a-1f7b-40ad-b4e2-12b298e85ccc","ownedRelatedElement":[{"id":"76806c6d-2d0a-4dc0-9e1c-23dfe0845699","eClass":"sysml:ReferenceUsage","data":{"elementId":"7413373c-e4ea-4187-bcbe-e4f93128dab3","ownedRelationship":[{"id":"e57840d9-94d8-4186-8bbc-e56d0b5532d9","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"2a9d2963-2b80-4ed3-8daf-6b3637ed343c","subsettingFeature":"76806c6d-2d0a-4dc0-9e1c-23dfe0845699","referencedFeature":"630f2641-b66a-4f52-91c6-057b1c5811d0"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"1912dc94-522f-4d2a-9b1b-84ca3d86491e","eClass":"sysml:ParameterMembership","data":{"elementId":"4c276cae-1333-4c4d-a0fc-609b74b86cd6","ownedRelatedElement":[{"id":"43bed746-a5d8-4961-9fa3-560b9e53d921","eClass":"sysml:ReferenceUsage","data":{"elementId":"9e08b023-69df-4262-92be-ce2b9c6339a0","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}},{"id":"560d42e6-c398-48cf-9816-1bd9cae87041","eClass":"sysml:FeatureMembership","data":{"elementId":"4b970e61-3c84-4cf2-9c1f-ea3ef53bb346","ownedRelatedElement":[{"id":"630f2641-b66a-4f52-91c6-057b1c5811d0","eClass":"sysml:StateUsage","data":{"declaredName":"normal","elementId":"59276fa3-50b8-45da-a7d0-1775b3632476","isComposite":true}}]}},{"id":"e1a46bc4-adad-4ecd-ac1f-ec293b4466da","eClass":"sysml:FeatureMembership","data":{"elementId":"ca2e7bf0-9cee-4679-b29c-60eeadc8f7c1","ownedRelatedElement":[{"id":"9390334a-f844-4352-91a5-f0fbcfa072f7","eClass":"sysml:StateUsage","data":{"declaredName":"heating","elementId":"502e583f-266e-4e03-82d5-9198951b9736","isComposite":true}}]}},{"id":"4486dadb-f285-43a9-b554-b5dbe1f30ec4","eClass":"sysml:FeatureMembership","data":{"elementId":"a87c855f-cbc1-441a-90f9-59427af39f5c","ownedRelatedElement":[{"id":"1ff2edd1-7ecc-4711-8e0b-1d9e6ba18afd","eClass":"sysml:StateUsage","data":{"declaredName":"cooling","elementId":"336fbd96-f982-4f5f-ba7b-611ca08daa02","isComposite":true}}]}},{"id":"b7c0db78-1dfc-4f68-aa58-72a367032fca","eClass":"sysml:FeatureMembership","data":{"elementId":"5027456b-92a5-468a-8dc3-4da4d77356e5","ownedRelatedElement":[{"id":"8e205929-279c-421f-b739-c4d498c114da","eClass":"sysml:AttributeUsage","data":{"declaredName":"currentTemp","elementId":"5e7f955d-d275-4a9f-a4f6-fd91e00c903a","ownedRelationship":[{"id":"5be10aa2-0fcf-4b76-81bc-c700d3889d97","eClass":"sysml:FeatureValue","data":{"elementId":"f590f069-37c2-4b72-b412-0c004e7b3969","ownedRelatedElement":[{"id":"d0dec34a-5aca-420e-b7d6-4530d7ca54b2","eClass":"sysml:LiteralRational","data":{"elementId":"22502ad2-6755-4a78-b001-ae7d823d7a1a","value":20.0}}],"isInitial":true}},{"id":"7f7aafa0-3fc2-4cb5-9aa0-81039d5d4a51","eClass":"sysml:FeatureTyping","data":{"elementId":"fdb43bab-e1cc-4c65-966d-8242a09d6c52","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"8e205929-279c-421f-b739-c4d498c114da"}}],"isComposite":true}}]}},{"id":"a6e0b47d-eb19-4328-b6d6-99e68bfed356","eClass":"sysml:FeatureMembership","data":{"elementId":"9fc5a32e-ece4-4122-bed7-51fc54c19ba1","ownedRelatedElement":[{"id":"541bd9c9-44fa-4440-bebc-3f84820674e8","eClass":"sysml:AttributeUsage","data":{"declaredName":"targetTemp","elementId":"2a8602b9-d12d-40a2-9ca4-eeb421a9e2ed","ownedRelationship":[{"id":"e375b8ff-ef56-407b-8538-52a31352d147","eClass":"sysml:FeatureValue","data":{"elementId":"b63a0860-3159-44cc-922e-e4e962e48d91","ownedRelatedElement":[{"id":"2c01bf77-f6e4-4677-92c3-945189119f2e","eClass":"sysml:LiteralRational","data":{"elementId":"1d6f8b13-f67a-4508-9514-0fdd42ce7a11","value":22.0}}]}},{"id":"de665ea7-84c3-4e42-9a77-f593fe17b134","eClass":"sysml:FeatureTyping","data":{"elementId":"46004252-bb31-4407-9d4d-1e02857d777e","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"541bd9c9-44fa-4440-bebc-3f84820674e8"}}],"isComposite":true}}]}},{"id":"8d07b2dd-cb18-4fc0-baa8-f651b807b46c","eClass":"sysml:FeatureMembership","data":{"elementId":"485d35c7-55b7-4e7c-807b-0c65402c854c","ownedRelatedElement":[{"id":"9e405dc0-36d0-47f8-8b8d-461ab5a31169","eClass":"sysml:AttributeUsage","data":{"declaredName":"tolerance","elementId":"c79fd2f6-b87e-45ab-a978-d6e365d1892b","ownedRelationship":[{"id":"30f1fde2-4f4b-4257-8a9d-851f11cc8e6f","eClass":"sysml:FeatureValue","data":{"elementId":"e8a2a7c8-3eb0-4eca-af46-8812cde65a6a","ownedRelatedElement":[{"id":"ae8ef32c-b7d9-4f25-992d-8abc484db859","eClass":"sysml:LiteralRational","data":{"elementId":"ea074070-969d-4a48-b05b-afed056dab84","value":1.0}}]}},{"id":"d4ff4acb-0756-4348-8f5a-ff6f5a4fed41","eClass":"sysml:FeatureTyping","data":{"elementId":"50c766d5-c266-474b-9c43-d2cc1c0c498d","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"9e405dc0-36d0-47f8-8b8d-461ab5a31169"}}],"isComposite":true}}]}},{"id":"7656692f-d1a8-4b63-9969-0191adc8273e","eClass":"sysml:FeatureMembership","data":{"elementId":"d8e1cc5d-5bfb-4195-91db-318eeb0b2706","ownedRelatedElement":[{"id":"0b517687-ef3f-4057-9ce5-27e27f627a22","eClass":"sysml:TransitionUsage","data":{"declaredName":"to_heating","elementId":"722d9bb6-f15a-4133-9baa-5d87384af8b2","ownedRelationship":[{"id":"ad20395d-9921-44c2-8311-8afdcdf04157","eClass":"sysml:Membership","data":{"elementId":"1d757d16-ec70-4b47-a753-7dd9402e02bc","memberElement":"630f2641-b66a-4f52-91c6-057b1c5811d0"}},{"id":"adcd33cb-6cb5-4b17-9c69-ad80369f135b","eClass":"sysml:TransitionFeatureMembership","data":{"elementId":"bd902655-656e-4d4f-ac6d-99ba34d0137a","ownedRelatedElement":[{"id":"c0d13f10-13cf-410a-93b7-69d1b4f4c087","eClass":"sysml:OperatorExpression","data":{"elementId":"909fc54a-ec14-443d-a7e9-1a9258b27d6d","ownedRelationship":[{"id":"e2e339c8-0f79-44c9-81cf-688da9cc86da","eClass":"sysml:ParameterMembership","data":{"elementId":"d91c3529-d8b8-43b0-a14b-232d2654054f","ownedRelatedElement":[{"id":"8f6d10d3-a15a-4834-a413-9081216bebce","eClass":"sysml:Feature","data":{"elementId":"3320621a-f1fc-4247-a403-a37ed55995b8","ownedRelationship":[{"id":"e1c4bd58-db2f-4848-87bf-fb137774e651","eClass":"sysml:FeatureValue","data":{"elementId":"1a3215c4-89a3-44af-b565-35507207997d","ownedRelatedElement":[{"id":"332f091f-0037-412f-8e8c-e9a304e1b10b","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"fdcc2e28-f97a-406e-83ab-d125e81e692d","ownedRelationship":[{"id":"e7276e34-09c6-4c65-86c7-697421caa946","eClass":"sysml:Membership","data":{"elementId":"e37224b5-82fd-45ec-9ba8-34c6fd9b69d4","memberElement":"8e205929-279c-421f-b739-c4d498c114da"}}]}}]}}],"direction":"in"}}]}},{"id":"cf645d64-56de-4213-90a2-9c4a6db8e579","eClass":"sysml:ParameterMembership","data":{"elementId":"63442128-6cfa-4e4a-ba44-3ce24316184c","ownedRelatedElement":[{"id":"b49feb80-5af2-4e5a-8ba5-dd65c5fa001d","eClass":"sysml:Feature","data":{"elementId":"3ec5ffa8-ecbc-4770-8842-43e4c6fd4d05","ownedRelationship":[{"id":"23ea0751-f45b-40c9-a30f-b147a0208019","eClass":"sysml:FeatureValue","data":{"elementId":"5c869a08-bebb-4042-9604-f5b376c7917a","ownedRelatedElement":[{"id":"402f380f-d296-4ad0-b9ae-43b308744a29","eClass":"sysml:OperatorExpression","data":{"elementId":"239f31d9-f8c8-4ac1-8d16-ecfb64248922","ownedRelationship":[{"id":"381565fe-0971-40f9-961f-316dfa6ee739","eClass":"sysml:ParameterMembership","data":{"elementId":"a3f78dbb-87b2-4ee1-8b89-0ee00d06f7d9","ownedRelatedElement":[{"id":"7b1c47ea-588d-4e27-95bd-f176c085c045","eClass":"sysml:Feature","data":{"elementId":"ee708e17-b539-4407-9c46-d132f00bb033","ownedRelationship":[{"id":"1ed2f67c-7788-40bd-9720-a2303cdf6fb4","eClass":"sysml:FeatureValue","data":{"elementId":"71456a75-6736-4cd9-86ca-f77f486fa746","ownedRelatedElement":[{"id":"8d70366b-37c0-455f-8e68-f598d82f77de","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"a39ef57d-5d4b-4e49-8c21-a81da65fac02","ownedRelationship":[{"id":"3bf0be2e-137b-4b79-820d-b78751628033","eClass":"sysml:Membership","data":{"elementId":"82a0c8ec-b7b1-4b64-9ca2-ff0ab64a1c93","memberElement":"541bd9c9-44fa-4440-bebc-3f84820674e8"}}]}}]}}],"direction":"in"}}]}},{"id":"3f51c431-426e-4fe0-8a8f-17ba1ac341fb","eClass":"sysml:ParameterMembership","data":{"elementId":"07ce41f0-2342-4371-b8ba-d5db86f2260b","ownedRelatedElement":[{"id":"fe0a71f5-757e-4197-8b98-abccdd56c125","eClass":"sysml:Feature","data":{"elementId":"c90febc4-f41d-4181-a166-47beec2aa460","ownedRelationship":[{"id":"54397d0d-b73d-4062-8384-852d5dd6c98c","eClass":"sysml:FeatureValue","data":{"elementId":"93c019ad-a7ea-42be-904d-ec1acb7d237e","ownedRelatedElement":[{"id":"56513cbf-c7da-496a-8980-edd49dd33bda","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"f3b3bb11-8087-4d66-8d58-dc3ea543897f","ownedRelationship":[{"id":"2040e3cd-2cbd-494b-a2a8-4c562f415347","eClass":"sysml:Membership","data":{"elementId":"a5c43209-6b39-40a0-ba6d-ed8298ac10cd","memberElement":"9e405dc0-36d0-47f8-8b8d-461ab5a31169"}}]}}]}}],"direction":"in"}}]}}],"operator":"-"}}]}}],"direction":"in"}}]}}],"operator":"<"}}],"kind":"guard"}},{"id":"e361c51a-2480-42e1-a181-ce7d079b2e93","eClass":"sysml:OwningMembership","data":{"elementId":"c2b19814-61f6-4e6e-938f-9cf8dd68ef62","ownedRelatedElement":[{"id":"8085cacc-ccab-4176-91bc-f90d138bf7f3","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"25f126b9-bc1f-43b5-8f65-6c11f41c569b","ownedRelationship":[{"id":"bc9dc7b9-9b80-4c4d-80e8-b137d62abf73","eClass":"sysml:EndFeatureMembership","data":{"elementId":"99db0664-a0b4-4ea0-87eb-cb801838d5fe","ownedRelatedElement":[{"id":"93e43e08-43e2-4c01-b101-436fefc0feb7","eClass":"sysml:ReferenceUsage","data":{"elementId":"430bfb97-e843-4c1d-9351-ed828370dcbc","isEnd":true}}]}},{"id":"45680a1a-a4e5-4f8b-933a-2f87141422ce","eClass":"sysml:EndFeatureMembership","data":{"elementId":"cdb2a753-f470-47a3-b073-f980d304fb23","ownedRelatedElement":[{"id":"7fd10dad-a085-472c-b06d-3173f81373b0","eClass":"sysml:ReferenceUsage","data":{"elementId":"28801261-d808-4b8b-a9c4-849aa076620f","ownedRelationship":[{"id":"ca8fd8cd-1fd0-4936-a7e8-474780da6295","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"6bf78264-9a29-4a7d-9f58-af4519b5a245","subsettingFeature":"7fd10dad-a085-472c-b06d-3173f81373b0","referencedFeature":"9390334a-f844-4352-91a5-f0fbcfa072f7"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"50ce6fd3-4386-43e7-9c79-92791f484058","eClass":"sysml:ParameterMembership","data":{"elementId":"4b1fa532-9975-475f-b7a3-2b8dccde5efa","ownedRelatedElement":[{"id":"a50dc602-495e-4cbf-8587-a7909d1e064d","eClass":"sysml:ReferenceUsage","data":{"elementId":"918f482a-795f-4158-9f1b-16f94c2cfb4a","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}},{"id":"211a29e1-2104-4473-bc7a-e7675162e447","eClass":"sysml:FeatureMembership","data":{"elementId":"30842099-ac84-4baf-aae8-d78c11ba491a","ownedRelatedElement":[{"id":"e1c27d0b-476e-42d4-9b81-b5f064d9cbbe","eClass":"sysml:TransitionUsage","data":{"declaredName":"to_cooling","elementId":"3df50466-18d3-48d1-9b1a-6b35ab79f2b7","ownedRelationship":[{"id":"6971555b-4116-48f0-9847-9292f1357d54","eClass":"sysml:Membership","data":{"elementId":"1ae63504-5879-44d8-845c-58145c66cee8","memberElement":"630f2641-b66a-4f52-91c6-057b1c5811d0"}},{"id":"2f3331f5-bbe1-44c4-844b-d24c8f2eeffb","eClass":"sysml:TransitionFeatureMembership","data":{"elementId":"f0e1ee9a-8235-4875-b5e3-1ff7f8ac8a85","ownedRelatedElement":[{"id":"ec67ead5-7739-4dd3-a82e-f1bb5ef34268","eClass":"sysml:OperatorExpression","data":{"elementId":"974ce809-d343-4a2f-8fe5-cec41eb46ba4","ownedRelationship":[{"id":"371effd8-5eea-4802-94e5-704498a68b13","eClass":"sysml:ParameterMembership","data":{"elementId":"bc3ab140-50b6-4c33-9874-23ebc399610c","ownedRelatedElement":[{"id":"40353cdf-d10b-4322-b623-05972ba837e3","eClass":"sysml:Feature","data":{"elementId":"664143da-4921-42a1-9cfd-cfc893719859","ownedRelationship":[{"id":"1eea43cc-f48e-4dc3-bf5d-574dd7fadcb9","eClass":"sysml:FeatureValue","data":{"elementId":"4f6f4e36-88e9-4ce7-b53f-2bb46878a9f3","ownedRelatedElement":[{"id":"1440ba96-39f9-4745-9596-625baab83480","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"a06418d2-453e-4ea7-90c6-cedb31a54a93","ownedRelationship":[{"id":"3af419a1-d8cd-4f4e-a95f-03d88df310c5","eClass":"sysml:Membership","data":{"elementId":"94b074ff-c911-4b46-838c-d617bc424d2d","memberElement":"8e205929-279c-421f-b739-c4d498c114da"}}]}}]}}],"direction":"in"}}]}},{"id":"b6bbf397-915c-4df2-8153-046eddf48b60","eClass":"sysml:ParameterMembership","data":{"elementId":"1c049222-6ff6-4835-a00a-8df9349d8266","ownedRelatedElement":[{"id":"e73a1795-d083-4539-953e-6ce302c9c7a0","eClass":"sysml:Feature","data":{"elementId":"4b579ae4-8c79-4c88-9b21-db059d7857b7","ownedRelationship":[{"id":"74a4b0ca-269e-480b-b209-5a36bcfcad18","eClass":"sysml:FeatureValue","data":{"elementId":"5a8e236c-908c-478f-94a4-1dccce775e9f","ownedRelatedElement":[{"id":"c656679c-b3f8-4218-8b7c-94f40c432e9e","eClass":"sysml:OperatorExpression","data":{"elementId":"e7eac6ca-3967-41af-8f50-de765b9e7c87","ownedRelationship":[{"id":"90b4fda8-1e61-4157-966d-60ebaa3cecaa","eClass":"sysml:ParameterMembership","data":{"elementId":"8dfcdc00-48ae-4f73-b096-74263b2ff799","ownedRelatedElement":[{"id":"c612b8d5-4885-4c2e-85ed-6d0ad2074202","eClass":"sysml:Feature","data":{"elementId":"c1768702-c8ce-41c2-8c51-8195d64c97b6","ownedRelationship":[{"id":"03915a4e-b0da-48e8-863f-30f53ac37cea","eClass":"sysml:FeatureValue","data":{"elementId":"b00bb56f-0b71-407c-baa4-90956eb99b76","ownedRelatedElement":[{"id":"13070906-e608-4e8d-8418-3fc3916128f0","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"882ab32c-e241-4989-955c-608a9acbae32","ownedRelationship":[{"id":"9726f328-2238-44c6-b640-29ebad59d238","eClass":"sysml:Membership","data":{"elementId":"bc37097c-d761-4b2d-b506-9c807f25c907","memberElement":"541bd9c9-44fa-4440-bebc-3f84820674e8"}}]}}]}}],"direction":"in"}}]}},{"id":"3d67e9aa-3fc0-4e48-a7a0-632f6b5730c1","eClass":"sysml:ParameterMembership","data":{"elementId":"a56ba801-d9c5-4302-99ae-d5e68376378c","ownedRelatedElement":[{"id":"870fc619-a861-4d71-8b0d-4c700c8c4b3f","eClass":"sysml:Feature","data":{"elementId":"bd432a49-148b-4805-b314-6aaa2b485eba","ownedRelationship":[{"id":"db0aabd1-5712-4faa-86b7-4bd4176b4389","eClass":"sysml:FeatureValue","data":{"elementId":"9702fb41-ed29-4393-bbba-8c6b864eb4e0","ownedRelatedElement":[{"id":"edc353c4-b66d-4b2b-b8d5-55f8246922ab","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"82a521eb-14f5-4e56-be04-49bb2a83678d","ownedRelationship":[{"id":"7e2b0f6f-6c10-4670-aa85-2294aadb7be3","eClass":"sysml:Membership","data":{"elementId":"225dfdb5-3b07-4afd-bb2d-554318c82ed5","memberElement":"9e405dc0-36d0-47f8-8b8d-461ab5a31169"}}]}}]}}],"direction":"in"}}]}}],"operator":"+"}}]}}],"direction":"in"}}]}}],"operator":">"}}],"kind":"guard"}},{"id":"f2dd1298-dec2-44c6-bccc-c6aff0942073","eClass":"sysml:OwningMembership","data":{"elementId":"c405c6cb-696d-4a3f-99e7-6dab852ae228","ownedRelatedElement":[{"id":"c4e5cb98-07df-49a6-a3b1-93e9d9d6f4f5","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"acd50047-bcc8-421f-b204-cef2c540355e","ownedRelationship":[{"id":"73144371-94ed-44bb-95ab-70a1a23360fe","eClass":"sysml:EndFeatureMembership","data":{"elementId":"697afe9a-c1f3-4c65-a063-7bc9ab4b106d","ownedRelatedElement":[{"id":"220c6106-c36f-4497-88e2-42125a9f69d5","eClass":"sysml:ReferenceUsage","data":{"elementId":"b1438ef2-513a-43b5-a292-7e34cd24f941","isEnd":true}}]}},{"id":"39c31f59-67df-408c-bf92-9b1c9dc209df","eClass":"sysml:EndFeatureMembership","data":{"elementId":"7e270dc1-76ee-4023-9d55-128523dd8d26","ownedRelatedElement":[{"id":"9dd8948f-9a7c-4682-bc46-dda6eaf7eed8","eClass":"sysml:ReferenceUsage","data":{"elementId":"8a705bce-88f0-4558-b010-cac84b264413","ownedRelationship":[{"id":"16928b68-0b7c-4da8-891f-bf6fc174361a","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"36718153-4eb3-48ca-8023-b1ca8a606897","subsettingFeature":"9dd8948f-9a7c-4682-bc46-dda6eaf7eed8","referencedFeature":"1ff2edd1-7ecc-4711-8e0b-1d9e6ba18afd"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"6b679136-5121-49fc-b63f-edea84f72f73","eClass":"sysml:ParameterMembership","data":{"elementId":"9d7e2288-1a5a-4ad9-aa04-96f72ddca831","ownedRelatedElement":[{"id":"5d11e636-89f5-4d42-a233-207b6d62d418","eClass":"sysml:ReferenceUsage","data":{"elementId":"8ffbe3d2-f907-4fd2-b2b3-c67ac8ef75bd","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}},{"id":"da3ff9dc-93bb-406a-a2cb-6bb17b551a62","eClass":"sysml:FeatureMembership","data":{"elementId":"6cf8e042-d8f9-41b6-9a1d-3232943b2b14","ownedRelatedElement":[{"id":"c7efc3c8-830f-41f4-a9cb-a3667f7c7637","eClass":"sysml:TransitionUsage","data":{"declaredName":"heating_done","elementId":"6bb2e424-424c-44d4-afb6-3923e8202303","ownedRelationship":[{"id":"c2f144d8-86b6-4dd7-b2ff-7280703d9e5d","eClass":"sysml:Membership","data":{"elementId":"551669f0-c179-4998-8de3-84a03f2255b4","memberElement":"9390334a-f844-4352-91a5-f0fbcfa072f7"}},{"id":"6dac8578-e793-4cc2-9581-59c2ef40532c","eClass":"sysml:TransitionFeatureMembership","data":{"elementId":"cf32b8b1-18ec-4787-9eb0-95da2eeb675a","ownedRelatedElement":[{"id":"e9f31b7a-9673-4037-859b-7d6e5775db53","eClass":"sysml:OperatorExpression","data":{"elementId":"514158d0-9ec0-4ca8-8cf6-c201eded035b","ownedRelationship":[{"id":"2b0b4608-771a-4b49-8264-ee9022d5f533","eClass":"sysml:ParameterMembership","data":{"elementId":"c32a9d2f-ed2f-442c-9eef-7f282e73f828","ownedRelatedElement":[{"id":"f9b860f0-3ab9-4b35-a5af-7b87c85aa51a","eClass":"sysml:Feature","data":{"elementId":"be79d529-4b62-4620-96f6-151347ef888a","ownedRelationship":[{"id":"06a57d10-f450-4edc-8d6d-7e9c085d5678","eClass":"sysml:FeatureValue","data":{"elementId":"b8b2fe56-0ab6-41c9-ab2a-fc9f238d4c36","ownedRelatedElement":[{"id":"07ecaa6c-9469-4602-9938-ef4fe35c8e61","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"498a0218-a6a1-4d1d-951d-b9f2fb76a93a","ownedRelationship":[{"id":"2a962769-9796-46c5-a084-e78784f6e775","eClass":"sysml:Membership","data":{"elementId":"4cf777d6-945d-407c-a1ed-e0b1af63fe54","memberElement":"8e205929-279c-421f-b739-c4d498c114da"}}]}}]}}],"direction":"in"}}]}},{"id":"925a2b9f-4c32-49a1-9b83-23b44f262ae8","eClass":"sysml:ParameterMembership","data":{"elementId":"2b1b4a8e-8d3e-4611-9e2b-e67fb634a03c","ownedRelatedElement":[{"id":"2a0e96be-a356-424d-a497-1fb6e0a67a75","eClass":"sysml:Feature","data":{"elementId":"b118b738-974f-47c8-9000-e676ff11acf9","ownedRelationship":[{"id":"77fa7bc6-7751-48fd-b4db-811e8ddb0107","eClass":"sysml:FeatureValue","data":{"elementId":"0faa3816-569c-44c2-91f4-0a62629bdc35","ownedRelatedElement":[{"id":"8deba717-e9e8-4359-a7ee-0392cc60da28","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"4f0cbbb1-ed40-47c5-9fdc-716954416eca","ownedRelationship":[{"id":"31c29b60-785d-4c12-91ff-1a6bd5cd0a97","eClass":"sysml:Membership","data":{"elementId":"cfb554e6-b753-40aa-8212-646ac2307d97","memberElement":"541bd9c9-44fa-4440-bebc-3f84820674e8"}}]}}]}}],"direction":"in"}}]}}],"operator":">="}}],"kind":"guard"}},{"id":"52bd3814-e962-4bf3-bdbc-af103344c0ad","eClass":"sysml:OwningMembership","data":{"elementId":"68dca418-736b-429d-b442-271730f62bed","ownedRelatedElement":[{"id":"bf42847e-36cd-4d3d-aa5f-adbda8deedf1","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"1235177c-931e-444e-841a-74a0033b7573","ownedRelationship":[{"id":"71fc2ef3-58ee-46f5-9f59-16782d9d18ca","eClass":"sysml:EndFeatureMembership","data":{"elementId":"64387b5f-6f0c-43c6-8630-9f507376e5e3","ownedRelatedElement":[{"id":"445af89b-6f69-4a1b-bb66-ee2fad4012bd","eClass":"sysml:ReferenceUsage","data":{"elementId":"39473346-c45b-4bf0-a667-771e453ca497","isEnd":true}}]}},{"id":"853b2327-fd95-4281-92d3-39675c9832ff","eClass":"sysml:EndFeatureMembership","data":{"elementId":"ae636aba-2171-48b4-9bc9-36b8e319a679","ownedRelatedElement":[{"id":"b2b75688-a923-4a1f-966d-bacdff99b033","eClass":"sysml:ReferenceUsage","data":{"elementId":"a4debe8e-e4c5-4e03-ac70-94f40d27f9e9","ownedRelationship":[{"id":"43ce1363-6e62-4981-b36e-b8e0b32431f3","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"66ec302b-b8c2-4029-8536-830eeb9904ae","subsettingFeature":"b2b75688-a923-4a1f-966d-bacdff99b033","referencedFeature":"630f2641-b66a-4f52-91c6-057b1c5811d0"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"a4f9b12e-4854-450c-a5b4-c13417654040","eClass":"sysml:ParameterMembership","data":{"elementId":"46d97c31-de66-482e-91da-e7fbc6555f7a","ownedRelatedElement":[{"id":"f7e4f511-9110-424d-9787-c4bec859f3bb","eClass":"sysml:ReferenceUsage","data":{"elementId":"e1cdc0e3-a709-4173-9d58-7500c52b4d9f","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}},{"id":"fd55211f-bc7f-4598-8bc1-0a3d4b54f974","eClass":"sysml:FeatureMembership","data":{"elementId":"888f6364-e8aa-4312-8477-baa01db1b493","ownedRelatedElement":[{"id":"8a153941-bf0a-4010-a51e-62acb8bd63bb","eClass":"sysml:TransitionUsage","data":{"declaredName":"cooling_done","elementId":"9bb3b3d0-59ae-43ea-aeeb-27cd95233987","ownedRelationship":[{"id":"5a798e86-8ac9-428c-bd43-983a008a96ce","eClass":"sysml:Membership","data":{"elementId":"150daea0-ca87-4737-b1cc-be68c0708d45","memberElement":"1ff2edd1-7ecc-4711-8e0b-1d9e6ba18afd"}},{"id":"9a4869bf-59f1-40fe-bb41-5b80125eec95","eClass":"sysml:TransitionFeatureMembership","data":{"elementId":"0ee132d8-b870-40a6-93b2-0e386ca698ec","ownedRelatedElement":[{"id":"506f2589-c9aa-497d-b9e2-9f3568a1b60e","eClass":"sysml:OperatorExpression","data":{"elementId":"b17dcb88-0404-4a2d-b175-29b5c2430b10","ownedRelationship":[{"id":"9439080c-7bc5-442c-a234-4fbced1722fd","eClass":"sysml:ParameterMembership","data":{"elementId":"03eeff23-1ce5-40e9-b8f8-8c971d8fea9b","ownedRelatedElement":[{"id":"4bb65cfe-8311-4ac1-85d8-1b19421553b0","eClass":"sysml:Feature","data":{"elementId":"12652e9b-2669-4e27-90fb-3d6c3f3a665b","ownedRelationship":[{"id":"6ec15f89-da66-4dc9-bdf1-34a8235a0e60","eClass":"sysml:FeatureValue","data":{"elementId":"516566a3-89e8-4057-8bb4-3238a772efae","ownedRelatedElement":[{"id":"131bcb2c-6bd2-4e48-a392-4bc83d15ec5d","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"71069a61-0947-49ae-82a6-131855801506","ownedRelationship":[{"id":"3b4c7e1b-00ad-4ef4-8f12-87c642d7e340","eClass":"sysml:Membership","data":{"elementId":"b7da76e8-90dd-4816-a096-3bc4d74fcfff","memberElement":"8e205929-279c-421f-b739-c4d498c114da"}}]}}]}}],"direction":"in"}}]}},{"id":"4c107a4c-9209-412b-953e-60b1c643382a","eClass":"sysml:ParameterMembership","data":{"elementId":"4e92e729-b2d1-424c-9471-8f8c17cdc6b7","ownedRelatedElement":[{"id":"010203af-dce4-454e-a19a-888ae9c3b675","eClass":"sysml:Feature","data":{"elementId":"401748ba-f2fe-4fd0-903a-bbbfb3067510","ownedRelationship":[{"id":"4ba22430-f1f1-4462-a196-ed8b6e119841","eClass":"sysml:FeatureValue","data":{"elementId":"9854172d-ee15-4e79-9acd-9717b3b5c598","ownedRelatedElement":[{"id":"61a15a33-8067-4d16-a44f-73bf1eb99e2d","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"972b0f23-2c30-42c6-93d4-71b2d291460e","ownedRelationship":[{"id":"364f5f78-67b1-4631-9d0e-01641d95afc9","eClass":"sysml:Membership","data":{"elementId":"cfba9064-3b74-4bf4-bdbf-97e54c8b2013","memberElement":"541bd9c9-44fa-4440-bebc-3f84820674e8"}}]}}]}}],"direction":"in"}}]}}],"operator":"<="}}],"kind":"guard"}},{"id":"0f19e6b9-2973-411c-b800-8d0ed49795be","eClass":"sysml:OwningMembership","data":{"elementId":"d2755907-2b2e-452f-8051-637f99928b24","ownedRelatedElement":[{"id":"d8aee92a-f512-4762-b4e4-b948f450a024","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"c1cb5168-b133-4e08-8f9f-3243ee80134d","ownedRelationship":[{"id":"9b1ee916-7707-43a1-937c-7cad3d9b5b77","eClass":"sysml:EndFeatureMembership","data":{"elementId":"1526df00-9a9b-41e4-8224-e54e782c429d","ownedRelatedElement":[{"id":"03e69081-de75-4755-8a05-056340bfcf21","eClass":"sysml:ReferenceUsage","data":{"elementId":"b907c9b6-3433-4113-9ed8-34644ca93a8c","isEnd":true}}]}},{"id":"f8423145-7a38-4e0b-848d-67a70db029c6","eClass":"sysml:EndFeatureMembership","data":{"elementId":"2595cd38-81ee-46c6-b87c-7c948a5ab166","ownedRelatedElement":[{"id":"98dfa1ca-9866-4079-b43d-b61af3c53e64","eClass":"sysml:ReferenceUsage","data":{"elementId":"ada1671c-beb1-4333-b144-1c9568840b05","ownedRelationship":[{"id":"629647f9-2a93-493e-883d-d5c0cb6d9fdd","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"de396f89-7470-469c-9ab6-a726f8de8400","subsettingFeature":"98dfa1ca-9866-4079-b43d-b61af3c53e64","referencedFeature":"630f2641-b66a-4f52-91c6-057b1c5811d0"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"5ed10d2c-ac2b-4864-9b4f-74218a8119d7","eClass":"sysml:ParameterMembership","data":{"elementId":"8444ae28-737f-4667-b584-a5ec3afd4cc5","ownedRelatedElement":[{"id":"3414baf1-cc30-4843-90e6-ed004d4b11f4","eClass":"sysml:ReferenceUsage","data":{"elementId":"a9f3e3d7-8086-467c-8a9f-040ae5ec8f4a","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}}]}}]}},{"id":"f1ac0d22-7a0f-413b-b576-85a3ffbe6135","eClass":"sysml:OwningMembership","data":{"elementId":"e648c25f-e278-4e65-9133-0dc96e1f0026","ownedRelatedElement":[{"id":"69d595d7-6a07-4cb1-a4ea-e6102b4ec12d","eClass":"sysml:ConcernDefinition","data":{"declaredName":"SafetyAndReliability","elementId":"3c2ae026-8af6-4bf4-bc7c-79234a98eff9","ownedRelationship":[{"id":"ef1499c9-c7f7-4acb-acf5-e8877c9383c8","eClass":"sysml:FeatureMembership","data":{"elementId":"c610ab68-ac78-4599-b05e-7174225eeaa7","ownedRelatedElement":[{"id":"e2678e9c-3155-4570-bd56-4c8457084f05","eClass":"sysml:AttributeUsage","data":{"declaredName":"deploymentCount","elementId":"9c8fd285-2a30-4ee8-9e2b-cc5a38f03e85","ownedRelationship":[{"id":"3e8dbff8-89e0-4113-a368-9080e6e59b45","eClass":"sysml:FeatureValue","data":{"elementId":"7359a844-4139-4401-b296-a9ab08f92acf","ownedRelatedElement":[{"id":"2db9e19e-7b70-420f-bbab-875dd36ee56f","eClass":"sysml:LiteralInteger","data":{"elementId":"05d4dfe9-b7c6-4b85-864b-83f347eee7be","value":3}}]}},{"id":"21d2afe7-e7b0-4994-9dca-09221ba31bf1","eClass":"sysml:FeatureTyping","data":{"elementId":"c6132f83-494c-4a60-bbac-1862a5261fc7","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#f2350199-2ab1-5258-8514-58812ef25dc6","typedFeature":"e2678e9c-3155-4570-bd56-4c8457084f05"}}],"isComposite":true}}]}},{"id":"5878dce7-7b5a-4f3f-9780-b01ccdd0c379","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"d788cb4d-7166-4e73-ac86-425afa925dec","ownedRelatedElement":[{"id":"5e36a9cd-89d3-4653-b767-0e1be2d32ad5","eClass":"sysml:ConstraintUsage","data":{"elementId":"585f8cab-50a5-47a5-8ea1-3f5929be4af2","ownedRelationship":[{"id":"821531fa-1c3b-468e-aeba-5f2f8ce6e2a4","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"a5488ef2-0227-4422-ac46-dda945fcbf4d","ownedRelatedElement":[{"id":"8a6c830c-98af-403f-a4de-938a31f61194","eClass":"sysml:OperatorExpression","data":{"elementId":"8f0efd41-c032-4a95-b4da-80641f15fa5a","ownedRelationship":[{"id":"298ac857-1424-421a-8895-f8b2ef5cfc55","eClass":"sysml:ParameterMembership","data":{"elementId":"337a51c2-5cb0-4c94-9309-06d838bdb875","ownedRelatedElement":[{"id":"b0ce01ae-8dd1-406b-b61e-6b5c0200a4bc","eClass":"sysml:Feature","data":{"elementId":"bed903ba-e17d-426e-bef1-3c83fed8ad5b","ownedRelationship":[{"id":"1489f1a6-db01-4f94-b7ba-3c221451fbbe","eClass":"sysml:FeatureValue","data":{"elementId":"294d19aa-e1a2-429a-a91e-35b068b48631","ownedRelatedElement":[{"id":"9e0eed6b-d20e-449a-b674-3e54e3d53859","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"4affe832-9f21-4775-abf7-9e33ea8c6c8e","ownedRelationship":[{"id":"37daf8ad-f0b9-4ac1-96f1-29da588d1fe5","eClass":"sysml:Membership","data":{"elementId":"bf9614cb-198c-4f53-9f63-0f1aa8820c10","memberElement":"e2678e9c-3155-4570-bd56-4c8457084f05"}}]}}]}}],"direction":"in"}}]}},{"id":"f35b3dda-8d34-4dc3-94e0-e5f7a7accff9","eClass":"sysml:ParameterMembership","data":{"elementId":"2bd75866-58d5-4522-aca1-6cf4196ed16b","ownedRelatedElement":[{"id":"1eace1ad-8813-4660-a1cd-332909552774","eClass":"sysml:Feature","data":{"elementId":"bd79950f-6dbf-4c42-8f2c-06aa4b7744c6","ownedRelationship":[{"id":"64d75861-f3c8-44f3-a0b8-dbf2e7878dc3","eClass":"sysml:FeatureValue","data":{"elementId":"07085d12-61df-40fa-8768-49c55c23e67b","ownedRelatedElement":[{"id":"56c8f7f8-93ef-4666-aa56-53b6531c0d4f","eClass":"sysml:LiteralInteger","data":{"elementId":"1791dfbd-3e30-488c-bbbe-30816bad15bc"}}]}}],"direction":"in"}}]}}],"operator":">"}}]}}],"isComposite":true}}]}},{"id":"597a0e23-3eef-471c-8dbd-75562a4e4d7a","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"b0aef8c1-d38c-4bd9-8ae2-4f0542fe91d7","ownedRelatedElement":[{"id":"b34a95e4-cb32-4a1c-b1ee-17d4bd62c7d2","eClass":"sysml:ConstraintUsage","data":{"declaredName":"minimumRedundancy","elementId":"76a7a9b1-9ecc-42b1-9a41-5b328053f12f","ownedRelationship":[{"id":"8c80e3db-82fa-41c4-be21-274b737f7284","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"dbb57839-6678-448a-8221-be6911b0f848","ownedRelatedElement":[{"id":"ed00c629-0f82-4874-9590-5a775e35081d","eClass":"sysml:OperatorExpression","data":{"elementId":"c67dae08-fad7-409c-ae7b-ee013cca30d6","ownedRelationship":[{"id":"7d801324-bad9-46b1-970f-7a2b38a556aa","eClass":"sysml:ParameterMembership","data":{"elementId":"8a478841-dab9-42a1-8966-ed68ec3be498","ownedRelatedElement":[{"id":"dc503057-d95b-4569-b694-9c03d8a4a711","eClass":"sysml:Feature","data":{"elementId":"5ee82585-9fb8-4395-8b39-e9ffc2ac6d80","ownedRelationship":[{"id":"8c469bc4-c47f-4ea8-9b6d-2aa4c643d7c1","eClass":"sysml:FeatureValue","data":{"elementId":"e1671e98-bc6d-4194-84c8-6f79c29850c0","ownedRelatedElement":[{"id":"2d5735e7-8a41-4fd8-856e-46af848a86f0","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"7f2be62b-42dc-4161-bbf1-9e33a596e6f7","ownedRelationship":[{"id":"cf8e2ed0-00d1-4e87-8a2f-b23d7c6c2879","eClass":"sysml:Membership","data":{"elementId":"f11aecf9-0c21-45a8-843f-fe5d4d96a0a9","memberElement":"e2678e9c-3155-4570-bd56-4c8457084f05"}}]}}]}}],"direction":"in"}}]}},{"id":"6f077a43-6f45-49b1-9232-b3282f0abb57","eClass":"sysml:ParameterMembership","data":{"elementId":"c2c6e7b8-af06-4096-989a-3c2e95baca51","ownedRelatedElement":[{"id":"0309d85e-37cc-499f-b876-846b2d5a4c2a","eClass":"sysml:Feature","data":{"elementId":"ca1bdcc4-dadc-4228-8d61-813b74f39cc7","ownedRelationship":[{"id":"dfad1497-873d-4629-a277-c254e1a247a9","eClass":"sysml:FeatureValue","data":{"elementId":"a50bbd08-8668-4de4-a64f-016da901a339","ownedRelatedElement":[{"id":"d1b9e2c7-f50f-44a3-a585-7f531eb3fbf9","eClass":"sysml:LiteralInteger","data":{"elementId":"48a9725a-a225-454e-8421-a03542e9e7e8","value":2}}]}}],"direction":"in"}}]}}],"operator":">="}}]}}],"isComposite":true}}],"kind":"requirement"}}]}}]}},{"id":"a6f07c1f-b22d-4081-a3d9-3fc78dee4d22","eClass":"sysml:OwningMembership","data":{"elementId":"db5e69df-ce41-43a4-a9c1-2655173fae74","ownedRelatedElement":[{"id":"da2296a4-25ed-4f04-abc8-d47f93cd223c","eClass":"sysml:ConcernDefinition","data":{"declaredName":"PerformanceConcern","elementId":"719defb0-780c-486b-85d5-b3d4e5b460cc","ownedRelationship":[{"id":"d1836f12-7281-4b5b-a0d2-26cfef749277","eClass":"sysml:SubjectMembership","data":{"elementId":"35bf0d61-c429-43fb-8f17-f6059aa0c4d3","ownedRelatedElement":[{"id":"33466a30-d081-4d0d-a3dc-142c59b82000","eClass":"sysml:ReferenceUsage","data":{"declaredName":"s","elementId":"ea6f2066-3a2f-4888-9eb8-33e6cec61b8d","ownedRelationship":[{"id":"1355d4f4-a4c7-4a0c-bb4a-a8dd29b2c3df","eClass":"sysml:FeatureTyping","data":{"elementId":"caea1502-7778-408f-ac2b-db317a07154f","type":"3f2124ff-5c56-486b-a296-b1c3476e5180","typedFeature":"33466a30-d081-4d0d-a3dc-142c59b82000"}}],"direction":"in","isComposite":true}}]}},{"id":"a4eda0c9-d3e4-4ebc-8d8b-0c864423acf4","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"fbab8f7d-eaf7-4510-bed3-2679d739229a","ownedRelatedElement":[{"id":"4fd2c402-15b7-4546-ad9c-7aa9419a1528","eClass":"sysml:ConstraintUsage","data":{"elementId":"98f1df4b-ff5c-498a-960b-9cb66ef0dbf9","ownedRelationship":[{"id":"36d9f176-7ed6-4905-b29d-6db221ad4a48","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"d31f27bd-7365-447b-8efe-fc63beb6a4cb","ownedRelatedElement":[{"id":"e0b5902b-0568-4ac2-86f8-9c99ee2ea99d","eClass":"sysml:OperatorExpression","data":{"elementId":"1bd5e29a-49b9-445e-8f28-a08c8f3789d2","ownedRelationship":[{"id":"2894d269-7449-43a5-b869-1c8ade17cfd4","eClass":"sysml:ParameterMembership","data":{"elementId":"c4e7842a-2eb7-4166-b2d4-d22629679d2d","ownedRelatedElement":[{"id":"3beee5fd-cf76-4519-8c61-83a455ab8844","eClass":"sysml:Feature","data":{"elementId":"d9bff176-b230-4c4c-acab-e984bf66cb07","ownedRelationship":[{"id":"acf8beeb-646a-428e-8468-7bf544fbca3e","eClass":"sysml:FeatureValue","data":{"elementId":"82fb5d89-009e-4f42-9c2a-a207d72de8cd","ownedRelatedElement":[{"id":"d5eac072-23fc-4597-af6e-4efeb7b922f3","eClass":"sysml:FeatureChainExpression","data":{"elementId":"8631de4f-c2a3-4a5e-abc4-f289fcad9cbf","ownedRelationship":[{"id":"00f7dd36-bc59-4888-98ca-cde32b4d2019","eClass":"sysml:ParameterMembership","data":{"elementId":"d6784867-db24-4ae7-a33a-51e3097ecf79","ownedRelatedElement":[{"id":"c6085eea-d204-47b7-9b9b-afbb857fa5eb","eClass":"sysml:Feature","data":{"elementId":"2305a2c5-7944-43d8-a2f3-7fca0cc8b13d","ownedRelationship":[{"id":"95f831d5-d054-428c-979f-370ab457876b","eClass":"sysml:FeatureValue","data":{"elementId":"db43790f-95ec-43ab-b828-6931bfec969d","ownedRelatedElement":[{"id":"9109e87b-a6be-4945-bb4b-fb5f5eb72182","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"1eeaf8dc-1714-4312-b916-a2f4150c49e3","ownedRelationship":[{"id":"99a0541b-69f4-4e6f-84e1-37726c94e2ef","eClass":"sysml:Membership","data":{"elementId":"e1933442-132c-465f-9089-97a232dbca20","memberElement":"33466a30-d081-4d0d-a3dc-142c59b82000"}}]}}]}}],"direction":"in"}}]}},{"id":"e67f2a70-47dd-4bd1-8df8-648804d9119e","eClass":"sysml:Membership","data":{"elementId":"c38669f1-fd86-4202-bb78-2d1aa53a8bb4","memberElement":"edc4f7bc-7d24-498e-8e76-ae1743668254"}}]}}]}}],"direction":"in"}}]}},{"id":"58d3fffa-c939-4a30-ae84-420ce97dba7a","eClass":"sysml:ParameterMembership","data":{"elementId":"c0a65af3-b764-47f5-8419-9d576135cbd1","ownedRelatedElement":[{"id":"91e41df5-a707-4754-adf1-7fb1319dc177","eClass":"sysml:Feature","data":{"elementId":"d89c649d-d83b-47ff-bf51-8e87eca905c6","ownedRelationship":[{"id":"a1e38bd6-5f5b-4551-8180-5901c46888cc","eClass":"sysml:FeatureValue","data":{"elementId":"18009365-ec4c-4f5f-a0c5-9545dad99c38","ownedRelatedElement":[{"id":"e84c5e93-fc46-4cf0-a0bf-f7cd4f7f16ce","eClass":"sysml:LiteralBoolean","data":{"elementId":"44d2e95b-1bef-4415-ac11-f0ff315c5445","value":true}}]}}],"direction":"in"}}]}}],"operator":"=="}}]}}],"isComposite":true}}]}},{"id":"934e8eac-2e71-4b04-b759-d928aee33336","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"df1c9c38-1d8b-49e6-b8f9-4cdc9af5b9d9","ownedRelatedElement":[{"id":"6891b4f5-6f02-4c73-ae23-db88ad10b253","eClass":"sysml:ConstraintUsage","data":{"declaredName":"responsiveness","elementId":"c0f01f9c-e59f-4517-9d2c-a295ce86c4b2","ownedRelationship":[{"id":"d20bfa80-aa30-436f-a5bf-96e2217efa58","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"33ea9c9a-383c-4d7e-b356-6df68a277de9","ownedRelatedElement":[{"id":"6f2e1644-b09d-46a2-b155-fc123e0f2953","eClass":"sysml:OperatorExpression","data":{"elementId":"378814e3-5c6b-4aac-983b-56d7ba918c1d","ownedRelationship":[{"id":"93a8bf8c-3c78-47f4-be7a-5bfe2faccaa3","eClass":"sysml:ParameterMembership","data":{"elementId":"8b7301cd-24b5-4b2b-8e1a-5a0dc096bdbc","ownedRelatedElement":[{"id":"fb7b2c32-7fe5-4ad7-bf3c-fc17280e4f4b","eClass":"sysml:Feature","data":{"elementId":"5a5849ab-2fa4-4966-8981-e252afcbe1f8","ownedRelationship":[{"id":"3a8347bc-70f7-4de8-892f-676542d63765","eClass":"sysml:FeatureValue","data":{"elementId":"dc48009e-02ff-4dd9-8986-71bef33e499f","ownedRelatedElement":[{"id":"501238ee-b170-4a71-add9-c3cc4b62b541","eClass":"sysml:OperatorExpression","data":{"elementId":"b4f2d2fc-f450-48f8-ade1-138bedd954ab","ownedRelationship":[{"id":"0e53a9bd-1bd5-4b12-931c-da6a744381be","eClass":"sysml:ParameterMembership","data":{"elementId":"2eecf130-286c-498a-b073-6ccd04e790dd","ownedRelatedElement":[{"id":"244833c3-2b41-467a-bb18-03bdca1aaf91","eClass":"sysml:Feature","data":{"elementId":"35c7ccb4-753a-4b3b-8bee-1e87f1faba9d","ownedRelationship":[{"id":"c2125fb7-4da1-4c18-ae2d-545b2ca6e3d3","eClass":"sysml:FeatureValue","data":{"elementId":"09b48893-6827-4cf9-bec6-88b807844c19","ownedRelatedElement":[{"id":"c19dca8a-1bed-49cd-a027-c10b6570e483","eClass":"sysml:FeatureChainExpression","data":{"elementId":"7a3c3fe4-404d-4889-9e34-1a3dd5ce5ca5","ownedRelationship":[{"id":"2febd014-6a70-48ac-9a28-211fb0cfc2bb","eClass":"sysml:ParameterMembership","data":{"elementId":"57bc8b4b-0a38-4b7f-b3a6-5d0523cc728c","ownedRelatedElement":[{"id":"a236b40b-3240-4162-8309-ba8de4a00b11","eClass":"sysml:Feature","data":{"elementId":"8a6e54a2-dd2a-46c2-840e-a0444dfc17d7","ownedRelationship":[{"id":"d4999cc0-72f8-418c-8a16-8d362577a450","eClass":"sysml:FeatureValue","data":{"elementId":"1371d3c0-f0db-4df2-b39a-86196bffcd6a","ownedRelatedElement":[{"id":"50e3db21-7e60-491c-ac6c-9a7dc9407207","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"4257cf6d-7f15-4fe9-b48e-24a072c6f040","ownedRelationship":[{"id":"87ec1ec4-0515-440b-b35c-8b8581217414","eClass":"sysml:Membership","data":{"elementId":"294b0f69-810d-4be2-bbdc-2022d8aa9794","memberElement":"33466a30-d081-4d0d-a3dc-142c59b82000"}}]}}]}}],"direction":"in"}}]}},{"id":"091ee316-159d-400d-94b8-ba0aa5c2f9b5","eClass":"sysml:Membership","data":{"elementId":"1dac782f-da3d-4fe4-9d47-3fa569692bcc","memberElement":"b922024b-5587-4be6-9924-6afa52ce8bf2"}}]}}]}}],"direction":"in"}}]}},{"id":"379e1891-786a-48ee-96a3-d6e3b00cde72","eClass":"sysml:ParameterMembership","data":{"elementId":"ef6e61be-424e-4063-bf2f-a81da5029fcc","ownedRelatedElement":[{"id":"fefc7b1f-cd73-4ef5-95bf-37f12b0749b8","eClass":"sysml:Feature","data":{"elementId":"d080d7d8-b587-4f09-8546-3b7515b73bf7","ownedRelationship":[{"id":"b08c94c0-5fd7-4498-81f8-91cc16df465e","eClass":"sysml:FeatureValue","data":{"elementId":"47f17548-693b-4b52-b6c5-146f93103b88","ownedRelatedElement":[{"id":"f8e0dcc5-6e48-4e37-a14d-37a1026ee01a","eClass":"sysml:LiteralRational","data":{"elementId":"39ac2b8c-4701-4616-8d73-6dc7ffe2c9cd","value":50.0}}]}}],"direction":"in"}}]}}],"operator":">="}}]}}],"direction":"in"}}]}},{"id":"eb8516c8-e8c8-4ee8-939a-55325c8b1cea","eClass":"sysml:ParameterMembership","data":{"elementId":"218a433e-cfa0-4902-9b4e-f0587be3b80e","ownedRelatedElement":[{"id":"c5b2f829-df1b-40ce-83d3-113b3bbfaba5","eClass":"sysml:Feature","data":{"elementId":"4134c6cb-8c93-4d25-af07-c284f7be3969","ownedRelationship":[{"id":"d60596d0-3c29-41a6-b460-898ed9b4c263","eClass":"sysml:FeatureValue","data":{"elementId":"7d1049c4-86cf-4589-b629-8d63970b8583","ownedRelatedElement":[{"id":"eb62764e-be00-4f53-a943-2bb9afe26d4c","eClass":"sysml:OperatorExpression","data":{"elementId":"00efad22-e113-41bc-be71-47de9a1fb9a8","ownedRelationship":[{"id":"64060226-0dc0-4dc1-8a5e-d01c98830213","eClass":"sysml:ParameterMembership","data":{"elementId":"300b058f-b25b-4281-90fe-684eb92606de","ownedRelatedElement":[{"id":"380808e7-4aa1-4f04-8296-8dbe5d1b9fc1","eClass":"sysml:Feature","data":{"elementId":"63ae1974-7814-4c0a-893e-72850334c386","ownedRelationship":[{"id":"23d8c3ac-ca81-45bb-956a-8e8597b49033","eClass":"sysml:FeatureValue","data":{"elementId":"8a290ed0-03f2-495d-bbc6-28f3570feb4b","ownedRelatedElement":[{"id":"4ad5fcc2-dcbe-4254-8e63-f4dc78552ad9","eClass":"sysml:OperatorExpression","data":{"elementId":"ff15a082-0091-4e71-bdf4-8cd81328f2ca","ownedRelationship":[{"id":"3949740d-1e51-4a76-81d9-66066d200c63","eClass":"sysml:ParameterMembership","data":{"elementId":"c63c1f9d-e626-47dc-9d32-59038df3b448","ownedRelatedElement":[{"id":"9a3ac2bf-cc5f-4854-87bc-14a035ad38e5","eClass":"sysml:Feature","data":{"elementId":"47d4f5d4-feb4-404c-9d39-e36cec21db8e","ownedRelationship":[{"id":"9d85853c-5921-4c83-a954-2d022e66f86d","eClass":"sysml:FeatureValue","data":{"elementId":"3f3a18b5-da04-4aa6-831c-984e8a65d47b","ownedRelatedElement":[{"id":"d3f88bd1-b325-4743-920b-e29c5806d7ef","eClass":"sysml:FeatureChainExpression","data":{"elementId":"f0d776b0-fd96-4282-aecd-722d89b96cff","ownedRelationship":[{"id":"89988082-a6b7-408a-99e5-9601ce0a1bf6","eClass":"sysml:ParameterMembership","data":{"elementId":"6ec39eba-a272-4db0-8c70-d16ea7517f86","ownedRelatedElement":[{"id":"51df5f6b-5cb6-48e7-9677-080c823eacca","eClass":"sysml:Feature","data":{"elementId":"53f44fae-071d-4263-9fde-7dc008d14df0","ownedRelationship":[{"id":"e741aa76-c4bc-4afc-95a6-d9d947a279c6","eClass":"sysml:FeatureValue","data":{"elementId":"a938aad2-2fb1-4258-b1c0-730ab660880b","ownedRelatedElement":[{"id":"ef20405c-9d4d-4ee5-bd10-b353bd9d8e1f","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"0e23e1a9-17f0-4ee1-bd4e-9ca406eece66","ownedRelationship":[{"id":"8938cb64-37ff-421a-82c4-3e1fbc43ddd4","eClass":"sysml:Membership","data":{"elementId":"4b50d9b9-0289-4edc-852e-b9037b2ef60f","memberElement":"33466a30-d081-4d0d-a3dc-142c59b82000"}}]}}]}}],"direction":"in"}}]}},{"id":"3045651a-778a-43ae-bdb0-b7072ec4bac1","eClass":"sysml:Membership","data":{"elementId":"1a41f919-9831-4a8b-b7ba-5c628414b769","memberElement":"6b9a0d65-eff4-4d98-8b25-d6e24d0486e9"}}]}}]}}],"direction":"in"}}]}},{"id":"fad2ff7a-212e-42e4-9a73-3f8c603d3d37","eClass":"sysml:ParameterMembership","data":{"elementId":"57db7214-0217-449d-8a9b-af78d0334a26","ownedRelatedElement":[{"id":"7c261030-6e18-4072-b4d8-5e700f06f982","eClass":"sysml:Feature","data":{"elementId":"a885a849-350f-41a8-acae-f10b40a21ae8","ownedRelationship":[{"id":"4fc87293-d300-4bdf-b824-c54f9fc9dd91","eClass":"sysml:FeatureValue","data":{"elementId":"1b1e8333-e3e8-4ebc-89b5-1a30a9f54b19","ownedRelatedElement":[{"id":"8d96afc1-c7dd-42ad-a350-57f0b800cc38","eClass":"sysml:LiteralRational","data":{"elementId":"576e66e8-67b6-49c2-957d-738dc65e0309"}}]}}],"direction":"in"}}]}}],"operator":"!="}}]}}],"direction":"in"}}]}},{"id":"03c4fde9-59c7-4499-b22c-f76128218acc","eClass":"sysml:ParameterMembership","data":{"elementId":"8b82cb63-e3d2-410a-86d5-053d9c4de34e","ownedRelatedElement":[{"id":"7949920d-fef9-4ada-a1fd-6146f740a9af","eClass":"sysml:Feature","data":{"elementId":"13999012-8669-458a-9a12-09a4344c25c5","ownedRelationship":[{"id":"7f0c0cbc-d241-42e4-9631-63195ec2387e","eClass":"sysml:FeatureValue","data":{"elementId":"4ae968bd-9835-490f-b122-70579a109558","ownedRelatedElement":[{"id":"4ca5a50e-d116-4b29-9e5c-bff45c6685d9","eClass":"sysml:OperatorExpression","data":{"elementId":"097d302b-bd39-4fdf-b37d-bfaf274d684a","ownedRelationship":[{"id":"c1b9246d-6c33-4efa-8349-63eca55ed258","eClass":"sysml:ParameterMembership","data":{"elementId":"acbda919-a449-4e50-8769-21c05c333f6e","ownedRelatedElement":[{"id":"7b99f3f9-9a46-4dc1-b31b-15b4f243e8e2","eClass":"sysml:Feature","data":{"elementId":"72c64d09-668a-4881-94f2-a02ee97e7f08","ownedRelationship":[{"id":"1ccd4617-8a24-40c1-b1bf-e14c57948d68","eClass":"sysml:FeatureValue","data":{"elementId":"df287639-e735-491a-8fe5-77e377244ea0","ownedRelatedElement":[{"id":"3b7a7f00-42b4-4962-8249-201ea3546292","eClass":"sysml:FeatureChainExpression","data":{"elementId":"39c623a8-db65-483c-b49d-6476f1eb02a7","ownedRelationship":[{"id":"c4b1bdfe-008a-42e2-a0da-560ef61c0657","eClass":"sysml:ParameterMembership","data":{"elementId":"dd1c5acd-02d0-40d5-a509-f934c915227d","ownedRelatedElement":[{"id":"f952d5b2-527e-4487-ad14-a501cbb4ec32","eClass":"sysml:Feature","data":{"elementId":"2302b015-7719-4042-9929-e4ad7cf7b1eb","ownedRelationship":[{"id":"ce4564b8-6fe7-49fc-a146-5d14b3c7405e","eClass":"sysml:FeatureValue","data":{"elementId":"6b6dae15-eda9-4734-8313-71897cfb4fe1","ownedRelatedElement":[{"id":"cbb045fe-e306-4bf8-84ed-e105822603f7","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"dac96cd8-1b64-4b8f-86d2-f9c162cb33a2","ownedRelationship":[{"id":"1fcde9a2-93af-4f46-b5af-0e6b2c94f94e","eClass":"sysml:Membership","data":{"elementId":"ec45938e-93b4-4039-8f0c-8fb5fae4bea0","memberElement":"33466a30-d081-4d0d-a3dc-142c59b82000"}}]}}]}}],"direction":"in"}}]}},{"id":"c3ae80a8-eeb0-4fd4-8d1a-36b18bf83520","eClass":"sysml:Membership","data":{"elementId":"b81cd454-88de-46fa-b993-db22a7a5d0bb","memberElement":"4d2f8465-d748-4b68-a5a8-d61c03031bd3"}}]}}]}}],"direction":"in"}}]}},{"id":"11ebb0eb-72cc-46a0-98f6-c135660b2e82","eClass":"sysml:ParameterMembership","data":{"elementId":"db89e0f5-6177-4b68-8850-44f7e32dd602","ownedRelatedElement":[{"id":"479d4b8c-c79b-42d3-805a-afc1dfa21852","eClass":"sysml:Feature","data":{"elementId":"83978f47-55dd-4399-a8b4-e84a7c03c13d","ownedRelationship":[{"id":"20aa93bd-a3fc-4985-b34a-c7d0cb4f8a98","eClass":"sysml:FeatureValue","data":{"elementId":"5ab031d1-3718-46a1-ae8c-c1eb2f226c3d","ownedRelatedElement":[{"id":"0f874cca-dc06-4430-89b8-a00dee51eaee","eClass":"sysml:LiteralInteger","data":{"elementId":"88c30f81-c3ce-4804-9142-c0b33f51dcd3"}}]}}],"direction":"in"}}]}}],"operator":"=="}}]}}],"direction":"in"}}]}}],"operator":"|"}}]}}],"direction":"in"}}]}}],"operator":"&"}}]}}],"isComposite":true}}],"kind":"requirement"}}]}}]}}]}}]}}]}}]}', '2026-04-28 09:22:34.945427+00', '2026-04-28 09:22:34.945427+00', false); +INSERT INTO public.document (id, semantic_data_id, name, content, created_on, last_modified_on, is_read_only) VALUES ('ef3e6929-0415-4295-b42f-64bed8bd4f55', 'aac82b89-8d53-4b16-91b4-deccc180ba62', 'expressions.sysml', '{"json":{"version":"1.0","encoding":"utf-8"},"ns":{"sysml":"http://www.eclipse.org/syson/sysml"},"migration":{"lastMigrationPerformed":"DiagramStyleDescriptionAddMigrationParticipant","migrationVersion":"2026.5.0-202603171430"},"content":[{"id":"152e5a5b-2b71-4653-8b7f-d46df76cd595","eClass":"sysml:Namespace","data":{"eAnnotations":[{"source":"org.eclipse.syson.sysml.imported"}],"elementId":"cfa2873f-f2f4-475a-8817-9f73e2dff74f","ownedRelationship":[{"id":"bea4a5f8-cc39-4cc0-ab5c-226fcf86f737","eClass":"sysml:OwningMembership","data":{"elementId":"eeda408c-b3e7-41af-a52b-3463777d41c0","ownedRelatedElement":[{"id":"baf5ea6a-7861-4b92-8be2-3fe7a2ebc415","eClass":"sysml:Package","data":{"declaredName":"Expressions","elementId":"6ff78b89-10b8-44f5-817f-50ea23e0c620","ownedRelationship":[{"id":"d8072b8b-400f-4968-b1e2-6601ba10bdc7","eClass":"sysml:NamespaceImport","data":{"elementId":"f2a55343-1f9c-4cc6-9912-8ca86dab734e","importedNamespace":"sysml:LibraryPackage kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#40bb440c-5036-58e1-8675-5afccb8b8f1d"}},{"id":"63ed369c-92bd-48cf-ba76-147c1ec13316","eClass":"sysml:OwningMembership","data":{"elementId":"033678d4-289b-46d1-9704-d97a006fb024","ownedRelatedElement":[{"id":"2bde22f7-a834-4afa-84bf-1bae1bb434d8","eClass":"sysml:PartDefinition","data":{"declaredName":"Tank","elementId":"e5e396e4-635f-44a2-8d85-50e19b94a6e7","ownedRelationship":[{"id":"bd7b6c14-0803-423c-b5d9-f368d8c32e6d","eClass":"sysml:FeatureMembership","data":{"elementId":"66064f96-7440-4236-bb21-11bb8c61224c","ownedRelatedElement":[{"id":"15ccc1e3-16b9-4ddf-9959-43141f32d48e","eClass":"sysml:AttributeUsage","data":{"declaredName":"pressure","elementId":"7479570e-234f-4603-a764-d45ba1228aad","ownedRelationship":[{"id":"470e47da-8c4f-45be-a841-5bb4fa82f8da","eClass":"sysml:FeatureTyping","data":{"elementId":"4f77e08f-1a50-4056-a6cb-283b6c0de857","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"15ccc1e3-16b9-4ddf-9959-43141f32d48e"}}],"isComposite":true}}]}},{"id":"27c51649-9b3b-471c-b846-33f10fe82332","eClass":"sysml:FeatureMembership","data":{"elementId":"747b5a9e-95de-4908-a7f0-3917cb4385ae","ownedRelatedElement":[{"id":"89b903a2-10c2-42a7-8307-b8bc9a4ba76a","eClass":"sysml:AttributeUsage","data":{"declaredName":"maxPressure","elementId":"058dca9f-5c49-4ae6-8f71-5e11df5b4021","ownedRelationship":[{"id":"01c173ba-5433-45f6-a9ad-5abf786562c9","eClass":"sysml:FeatureValue","data":{"elementId":"2a6cd6e7-6af0-426b-b6e9-1749f4cae266","ownedRelatedElement":[{"id":"703297e9-e397-496c-ac9a-49b2afff4ba7","eClass":"sysml:LiteralRational","data":{"elementId":"6a768b6c-2898-4dfa-aa7c-2a7f3a9fd6e4","value":600.0}}]}},{"id":"bb766cff-de2c-469f-90b8-06bd35514038","eClass":"sysml:FeatureTyping","data":{"elementId":"9e91a82c-3c8b-43f5-96eb-a92781f5a8d8","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"89b903a2-10c2-42a7-8307-b8bc9a4ba76a"}}],"isComposite":true}}]}},{"id":"77e243ce-1879-49a5-a4e6-ec8d91b8da9f","eClass":"sysml:FeatureMembership","data":{"elementId":"2a061597-9b75-422f-ac99-17317f4076a9","ownedRelatedElement":[{"id":"7c2ed44f-f991-4559-8594-cfd844160744","eClass":"sysml:AttributeUsage","data":{"declaredName":"volume","elementId":"87f09d7d-87a8-4edf-8c96-eb7d13c44702","ownedRelationship":[{"id":"9789b076-4604-4407-b662-6179ab76b2c5","eClass":"sysml:FeatureValue","data":{"elementId":"7625d338-d5ed-4050-bf6a-a22c75ea21af","ownedRelatedElement":[{"id":"a512cb6b-9395-48b9-88ba-fcb8b2754484","eClass":"sysml:LiteralRational","data":{"elementId":"cfa46233-fa49-4eef-b384-fc4a5861c39a","value":500.0}}]}},{"id":"91584942-a0c5-46f9-bd52-c252b9c0a285","eClass":"sysml:FeatureTyping","data":{"elementId":"cce82e52-823f-4f33-85ac-765af147c2f8","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"7c2ed44f-f991-4559-8594-cfd844160744"}}],"isComposite":true}}]}},{"id":"f5d0e91d-5d51-490e-93cf-42662bdb62e8","eClass":"sysml:FeatureMembership","data":{"elementId":"a3c78e1d-535a-4e89-a9cf-d97a00080b81","ownedRelatedElement":[{"id":"e7292249-00ff-41d1-a8b5-783668e88cc0","eClass":"sysml:AttributeUsage","data":{"declaredName":"minVolume","elementId":"6e6f68f1-9144-46ab-8b05-50d964f904a6","ownedRelationship":[{"id":"4c81d9e3-e5c9-4320-9cb1-395c353a73d1","eClass":"sysml:FeatureValue","data":{"elementId":"f861ccab-1624-4cee-ad26-1dfdc733163c","ownedRelatedElement":[{"id":"8e41e070-5926-4d74-9c76-d727d69d5fde","eClass":"sysml:LiteralRational","data":{"elementId":"d9800108-0bfd-4553-bd4a-ef16d51c1b38","value":10.0}}]}},{"id":"fcbae3ff-aeb7-48b2-97a0-55f8aa446d89","eClass":"sysml:FeatureTyping","data":{"elementId":"17d1e180-0632-4bec-9c10-0b166eb462f0","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"e7292249-00ff-41d1-a8b5-783668e88cc0"}}],"isComposite":true}}]}},{"id":"4c5f7f98-0312-4d3b-b2ac-b5a05fdb5e2f","eClass":"sysml:FeatureMembership","data":{"elementId":"b77a0d5e-7cf2-4978-a622-420c88455097","ownedRelatedElement":[{"id":"d9926727-7378-4177-a940-2fb6c1c89dce","eClass":"sysml:AttributeUsage","data":{"declaredName":"maxVolume","elementId":"c957466a-8bd4-448c-b2e5-545af7d9cadc","ownedRelationship":[{"id":"8f9592ef-9513-4025-ab13-e7d7f822f7d8","eClass":"sysml:FeatureValue","data":{"elementId":"5fd01a42-4bde-4a47-8b52-54a7c60049c1","ownedRelatedElement":[{"id":"a9216e54-f44c-4b1f-b262-13650324d325","eClass":"sysml:OperatorExpression","data":{"elementId":"ba3cea85-2725-4b96-a2af-a9159e84a7a9","ownedRelationship":[{"id":"b88c3a9d-0ce2-426e-a333-77225c45d429","eClass":"sysml:ParameterMembership","data":{"elementId":"4b58a948-936f-4567-9cb5-77739aee17c3","ownedRelatedElement":[{"id":"1ae9086c-60a8-4e6b-a24f-db44e021761b","eClass":"sysml:Feature","data":{"elementId":"19bfc3af-4154-4848-bd2e-59a1754b47df","ownedRelationship":[{"id":"52bad1f8-ee55-4bd5-aa2b-db12ec015c26","eClass":"sysml:FeatureValue","data":{"elementId":"0b26ad14-8872-4a9b-ac55-dac0f189df10","ownedRelatedElement":[{"id":"01ee6181-e890-4277-93fb-4bccf84bf3e6","eClass":"sysml:LiteralRational","data":{"elementId":"eb76a535-cb0d-4efd-b0f7-430b46d85d1a","value":100.0}}]}}],"direction":"in"}}]}},{"id":"8ed9cb6c-2fb5-43af-9bcf-9f2b83ea6e1b","eClass":"sysml:ParameterMembership","data":{"elementId":"c1df91fb-cea4-45f0-80a3-191171afb130","ownedRelatedElement":[{"id":"0222cbc2-571d-41ca-9aa6-5b7128062066","eClass":"sysml:Feature","data":{"elementId":"22361eb7-6a20-4f84-853a-cc4373032846","ownedRelationship":[{"id":"b31231a4-f853-4b71-ace4-eaab66065097","eClass":"sysml:FeatureValue","data":{"elementId":"49b00782-9d36-43a2-8a66-04d1e739daba","ownedRelatedElement":[{"id":"772d1b21-03f3-4299-aac5-564d787129b5","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"74181cea-a48b-4dbc-a45b-878f026c065f","ownedRelationship":[{"id":"576e21f5-e5b3-4a54-b6a7-636f4f2b7034","eClass":"sysml:Membership","data":{"elementId":"ef7b0f68-6c53-4d49-9537-eb3a561b1d91","memberElement":"e7292249-00ff-41d1-a8b5-783668e88cc0"}}]}}]}}],"direction":"in"}}]}}],"operator":"*"}}]}},{"id":"5f49fe65-264b-4773-9aa5-48ed839d4a86","eClass":"sysml:FeatureTyping","data":{"elementId":"c9ba2582-5b2d-4339-a4d9-edf1d05a350c","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"d9926727-7378-4177-a940-2fb6c1c89dce"}}],"isComposite":true}}]}},{"id":"bcb92dbe-1c98-4fbc-89bf-6a420f65d587","eClass":"sysml:FeatureMembership","data":{"elementId":"50960f37-4657-4ff4-9b24-01cf5d4839d5","ownedRelatedElement":[{"id":"7d42ee06-3c27-4eaa-9438-344fc789906a","eClass":"sysml:ConstraintUsage","data":{"declaredName":"pressureLimit","elementId":"0349f315-d32d-43e9-9627-9228da3bb552","ownedRelationship":[{"id":"d40b295e-159f-44fe-84a8-fa114dac3472","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"27e134d7-60b2-4a41-a2eb-ed1ca6c1a119","ownedRelatedElement":[{"id":"faa3b115-5b07-4ecf-9147-0d58ceffaf9c","eClass":"sysml:OperatorExpression","data":{"elementId":"d909007a-ddd7-4e31-b17a-4653dfa8cef8","ownedRelationship":[{"id":"94611d95-3c3d-45a8-813a-3b35933576e0","eClass":"sysml:ParameterMembership","data":{"elementId":"55d9251d-9722-4f1f-ade8-5cce8d7bafb1","ownedRelatedElement":[{"id":"a6e4adef-1a08-4384-880e-95da452d581a","eClass":"sysml:Feature","data":{"elementId":"d1eb3702-b4d0-48d4-90df-5354359f0d33","ownedRelationship":[{"id":"ce942576-087e-4f35-a0a3-7c23a7fe2bb0","eClass":"sysml:FeatureValue","data":{"elementId":"303c1de0-a072-43e8-b687-38527c51eb0e","ownedRelatedElement":[{"id":"83bc616a-bfb5-4777-a169-ac1f8142d2aa","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"046cd544-1c5c-450f-8fac-3077ff9d3652","ownedRelationship":[{"id":"ea9f50bb-2fc9-4934-b613-79a4bdf9ab0f","eClass":"sysml:Membership","data":{"elementId":"8f01928c-b0c1-4056-bfbb-64ca3335b251","memberElement":"15ccc1e3-16b9-4ddf-9959-43141f32d48e"}}]}}]}}],"direction":"in"}}]}},{"id":"7b4f5c1d-fbcd-4074-89bc-6928aecb206f","eClass":"sysml:ParameterMembership","data":{"elementId":"5ac072c4-4955-4803-b651-4d14c001f97e","ownedRelatedElement":[{"id":"41a595a8-9dfb-4542-936b-ba98891a4ea9","eClass":"sysml:Feature","data":{"elementId":"6f135276-6932-4db1-bd9c-126cb8475498","ownedRelationship":[{"id":"fb8829dc-0d44-4004-b7c6-2d617de26808","eClass":"sysml:FeatureValue","data":{"elementId":"73c691d9-1ad0-4357-92ae-8e02e690f5ef","ownedRelatedElement":[{"id":"15f2308f-9b14-4c40-b6a9-d496987dfc8b","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"45435f31-674c-40aa-ad49-ce170852a12b","ownedRelationship":[{"id":"78ed96c8-cc8b-488d-8970-524f4a8bd5be","eClass":"sysml:Membership","data":{"elementId":"5229ec54-8bd3-49e2-8221-a5f8a76142cc","memberElement":"89b903a2-10c2-42a7-8307-b8bc9a4ba76a"}}]}}]}}],"direction":"in"}}]}}],"operator":"<="}}]}}],"isComposite":true}}]}}]}}]}},{"id":"2c772a21-cd4b-472b-b9cb-2d291b86eff3","eClass":"sysml:OwningMembership","data":{"elementId":"2fcf9e17-3424-4677-b324-314c82774361","ownedRelatedElement":[{"id":"3f2124ff-5c56-486b-a296-b1c3476e5180","eClass":"sysml:PartDefinition","data":{"declaredName":"Sensor","elementId":"024ec40b-3a5b-46df-add7-4bcbec5523c7","ownedRelationship":[{"id":"560c46bc-4057-4b9e-8e5a-49e9e4109d05","eClass":"sysml:FeatureMembership","data":{"elementId":"7c0bc2eb-1488-4dd8-8ab9-afadef79a367","ownedRelatedElement":[{"id":"94c0a654-c5dc-4579-a655-89edda340104","eClass":"sysml:AttributeUsage","data":{"declaredName":"label","elementId":"46235c7e-3f38-4dfa-a061-fa5662c9ac1e","ownedRelationship":[{"id":"1758c3f6-d099-452d-8c1b-b375d51491fc","eClass":"sysml:FeatureValue","data":{"elementId":"4d70b01b-05c6-4646-b564-2035f4bde5be","ownedRelatedElement":[{"id":"ec30e68f-b6c2-4ab8-af03-9fea9ceaf2e3","eClass":"sysml:LiteralString","data":{"elementId":"1d905119-2ad9-41e5-8896-7c0d3f166e2b","value":"unnamed"}}]}},{"id":"6fd57c7e-3d50-49b3-94b7-133020804fc4","eClass":"sysml:FeatureTyping","data":{"elementId":"2a1b98bf-116f-44b6-a5cb-8aff68c8e1a8","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#76028d3d-69a4-5e12-9002-ce403e0244bd","typedFeature":"94c0a654-c5dc-4579-a655-89edda340104"}}],"isComposite":true}}]}},{"id":"ea09c1cf-a6fb-41a4-9a32-07f9a59d6ef4","eClass":"sysml:FeatureMembership","data":{"elementId":"c23a7f58-2c6b-45c3-bc49-1944baf5face","ownedRelatedElement":[{"id":"edc4f7bc-7d24-498e-8e76-ae1743668254","eClass":"sysml:AttributeUsage","data":{"declaredName":"enabled","elementId":"2aa70e33-7074-4c1d-8ef7-af6874857071","ownedRelationship":[{"id":"8cd41188-d199-4c49-ad23-10e366344c35","eClass":"sysml:FeatureValue","data":{"elementId":"7ea1ae0f-9769-4a93-8523-1b544171ffb4","ownedRelatedElement":[{"id":"5669db5e-ec61-40ca-9a52-8979126921d0","eClass":"sysml:LiteralBoolean","data":{"elementId":"14630a78-98b4-4a46-ac40-c8a49a7b7390","value":true}}]}},{"id":"2afde92b-9d7f-4e1c-ab29-bc9bd2967a82","eClass":"sysml:FeatureTyping","data":{"elementId":"0daf88b8-e462-482b-8220-4f19e3ce8380","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#d1e9242d-b2e3-5270-bf69-4f4fb0447193","typedFeature":"edc4f7bc-7d24-498e-8e76-ae1743668254"}}],"isComposite":true}}]}},{"id":"18f6bc41-fa40-4f81-b741-d601365a37a0","eClass":"sysml:FeatureMembership","data":{"elementId":"2b9522eb-72fb-4e5b-aed4-99209a770f92","ownedRelatedElement":[{"id":"b922024b-5587-4be6-9924-6afa52ce8bf2","eClass":"sysml:AttributeUsage","data":{"declaredName":"samplingRate","elementId":"ddec4b3c-42e0-4d8e-bd99-304def079d94","ownedRelationship":[{"id":"985de5ed-784f-465e-805b-6b7d88b3bddc","eClass":"sysml:FeatureValue","data":{"elementId":"5bb61755-a54e-4ea0-9ec7-e11adf867185","ownedRelatedElement":[{"id":"6d57d690-633a-4018-a0f6-5e732bc16897","eClass":"sysml:LiteralRational","data":{"elementId":"e5ef69e1-5daf-4dae-b0ed-1e7903b3d079","value":100.0}}]}},{"id":"0929e1d2-a563-4e37-befc-02767a3165ea","eClass":"sysml:FeatureTyping","data":{"elementId":"17f59ceb-baef-48a4-b67b-934ed58da6bf","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"b922024b-5587-4be6-9924-6afa52ce8bf2"}}],"isComposite":true}}]}},{"id":"48904b34-6744-4d90-8ea3-f8388f930416","eClass":"sysml:FeatureMembership","data":{"elementId":"a7691d9d-0037-4942-8a83-92cc93f14d7d","ownedRelatedElement":[{"id":"2f50920f-47d5-4508-a943-33f9a2d56a23","eClass":"sysml:AttributeUsage","data":{"declaredName":"channelCount","elementId":"4c2b5049-e9d0-4573-8fb7-032cbdcf8fdb","ownedRelationship":[{"id":"14910b2a-ff6e-4f7e-bb13-7e0979c49eca","eClass":"sysml:FeatureValue","data":{"elementId":"2d3e55d0-a26b-4a6b-ac99-f049047ada35","ownedRelatedElement":[{"id":"36055b10-a46d-42c9-95c2-901d02b7490a","eClass":"sysml:LiteralInteger","data":{"elementId":"4525ba90-45e6-4bb4-9a44-a2b3766560e8","value":4}}]}},{"id":"ffed96db-63ef-4e40-b41d-080f7dc68fef","eClass":"sysml:FeatureTyping","data":{"elementId":"c0034730-d919-4a37-ac87-9fc378662a0b","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#f2350199-2ab1-5258-8514-58812ef25dc6","typedFeature":"2f50920f-47d5-4508-a943-33f9a2d56a23"}}],"isComposite":true}}]}},{"id":"6292da99-b47c-48f9-8d1b-9115b1b0c4e9","eClass":"sysml:FeatureMembership","data":{"elementId":"c7efeeab-923d-42bc-9d62-8bf61c2ab2d7","ownedRelatedElement":[{"id":"6b9a0d65-eff4-4d98-8b25-d6e24d0486e9","eClass":"sysml:AttributeUsage","data":{"declaredName":"currentValue","elementId":"855d423f-5dbb-4042-b98c-5a34602b1d0e","ownedRelationship":[{"id":"d9b556a4-b6eb-4e20-90f6-e63908599859","eClass":"sysml:FeatureValue","data":{"elementId":"25fc714d-8228-45f3-98c1-d154cf129985","ownedRelatedElement":[{"id":"ba6195b6-3db6-410a-bf38-a22204c4263e","eClass":"sysml:LiteralRational","data":{"elementId":"31e7ced7-a849-497a-8bea-9e58b3969239"}}],"isInitial":true}},{"id":"0d5731f8-b287-4a2d-854b-32292f6082e2","eClass":"sysml:FeatureTyping","data":{"elementId":"9bb1f845-d859-45d9-b375-8749b0e46272","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"6b9a0d65-eff4-4d98-8b25-d6e24d0486e9"}}],"isComposite":true}}]}},{"id":"47e0493d-48fd-4a90-977b-f159c2e2dafe","eClass":"sysml:FeatureMembership","data":{"elementId":"7171788a-abcf-4895-8770-192412a650a6","ownedRelatedElement":[{"id":"4d2f8465-d748-4b68-a5a8-d61c03031bd3","eClass":"sysml:AttributeUsage","data":{"declaredName":"errorCount","elementId":"bfc48daf-6923-48f1-9d09-596579985dab","ownedRelationship":[{"id":"c0caf7ee-94b3-44f0-a03b-13c5bfa2f37a","eClass":"sysml:FeatureValue","data":{"elementId":"99cec056-4306-4921-805d-140ecc9ee5de","ownedRelatedElement":[{"id":"0e18cf3c-0d0f-4863-b4e2-407a9fcdad8c","eClass":"sysml:LiteralInteger","data":{"elementId":"cb488158-7555-424f-a37d-1c290aa3cee5"}}],"isInitial":true}},{"id":"96a56407-061b-4f40-81f5-4d8f5e211b56","eClass":"sysml:FeatureTyping","data":{"elementId":"db1c53ba-8888-4645-bbd1-4624a258a238","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#f2350199-2ab1-5258-8514-58812ef25dc6","typedFeature":"4d2f8465-d748-4b68-a5a8-d61c03031bd3"}}],"isComposite":true}}]}},{"id":"2b4f3de5-32fd-4859-9fe6-5689e017c120","eClass":"sysml:FeatureMembership","data":{"elementId":"24b2ddc3-2007-4fcd-a4b7-943e1bc43948","ownedRelatedElement":[{"id":"3f6b7e75-5039-485c-9fbc-776b772cd746","eClass":"sysml:AttributeUsage","data":{"declaredName":"active","elementId":"251cc6ef-d2c6-4893-941c-076709500c76","ownedRelationship":[{"id":"2ba77e81-8da1-4fdc-ba91-163988a8862a","eClass":"sysml:FeatureValue","data":{"elementId":"457e68ff-f795-4dd9-8643-2dd851d2387a","ownedRelatedElement":[{"id":"0e79ab60-92de-43dc-8aed-85bd61eaa2b7","eClass":"sysml:LiteralBoolean","data":{"elementId":"79052415-268e-41f4-8cf6-ac6484067e91"}}],"isInitial":true}},{"id":"bcd180b6-ecd8-47fc-8f28-08095cd9e23f","eClass":"sysml:FeatureTyping","data":{"elementId":"09b93323-05a1-429c-9dac-dc2672085d02","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#d1e9242d-b2e3-5270-bf69-4f4fb0447193","typedFeature":"3f6b7e75-5039-485c-9fbc-776b772cd746"}}],"isComposite":true}}]}}]}}]}},{"id":"0f77318f-48df-4fd5-b4fe-c2470070695a","eClass":"sysml:OwningMembership","data":{"elementId":"3f6dda74-d5cc-4bc5-b728-79fd8dc7a929","ownedRelatedElement":[{"id":"2efcf5d4-4948-4b7d-b779-f3f694eb8165","eClass":"sysml:StateDefinition","data":{"declaredName":"ThermalControl","elementId":"040718ea-26b5-47d6-ba84-9f7c43fe2c07","ownedRelationship":[{"id":"6bd335ce-5089-4089-902f-917b1b6fed36","eClass":"sysml:StateSubactionMembership","data":{"elementId":"ded89b53-c1f0-4c68-8a56-d7283bc2b862","ownedRelatedElement":[{"id":"ac85c0b8-2d5a-491f-81dc-fb9b09b3ab31","eClass":"sysml:ActionUsage","data":{"elementId":"fd138068-384e-4be8-b885-ef3b218e006f","isComposite":true}}],"kind":"entry"}},{"id":"0345c054-7f57-45ec-8473-1261313b14d6","eClass":"sysml:FeatureMembership","data":{"elementId":"ea20286f-4020-4488-bfdd-f8b8c0293486","ownedRelatedElement":[{"id":"0a63014b-116e-455e-85f5-bc66c922d691","eClass":"sysml:TransitionUsage","data":{"elementId":"ddef46f5-05d7-411e-b826-3f1f3fea21ff","ownedRelationship":[{"id":"baa2a68d-e8f5-470c-b497-eda44fe94d59","eClass":"sysml:Membership","data":{"elementId":"0ea170b2-1597-4c55-b3e8-6cf8b7a9d204","memberElement":"ac85c0b8-2d5a-491f-81dc-fb9b09b3ab31"}},{"id":"0e6d29f8-3a63-48d1-af12-339e173ae602","eClass":"sysml:OwningMembership","data":{"elementId":"f5f627e0-538f-4fd9-b64b-a14966f22482","ownedRelatedElement":[{"id":"c59c0174-da0a-4972-a5ca-6743ab295651","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"f55e815c-45ae-4320-9753-a362b3c8e6e5","ownedRelationship":[{"id":"38f392ae-b052-4618-8c6d-8c01e635a521","eClass":"sysml:EndFeatureMembership","data":{"elementId":"8f15832d-752e-417c-a176-520904b7dfe1","ownedRelatedElement":[{"id":"a2b00582-c35c-41cf-86c2-49ab6a853807","eClass":"sysml:ReferenceUsage","data":{"elementId":"293900dd-1f99-49d4-8638-8c541b96ed6b","isEnd":true}}]}},{"id":"bf9eb61a-6c2d-4a2e-af99-6aec971b7e95","eClass":"sysml:EndFeatureMembership","data":{"elementId":"067cb51a-1f7b-40ad-b4e2-12b298e85ccc","ownedRelatedElement":[{"id":"76806c6d-2d0a-4dc0-9e1c-23dfe0845699","eClass":"sysml:ReferenceUsage","data":{"elementId":"7413373c-e4ea-4187-bcbe-e4f93128dab3","ownedRelationship":[{"id":"e57840d9-94d8-4186-8bbc-e56d0b5532d9","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"2a9d2963-2b80-4ed3-8daf-6b3637ed343c","subsettingFeature":"76806c6d-2d0a-4dc0-9e1c-23dfe0845699","referencedFeature":"630f2641-b66a-4f52-91c6-057b1c5811d0"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"1912dc94-522f-4d2a-9b1b-84ca3d86491e","eClass":"sysml:ParameterMembership","data":{"elementId":"4c276cae-1333-4c4d-a0fc-609b74b86cd6","ownedRelatedElement":[{"id":"43bed746-a5d8-4961-9fa3-560b9e53d921","eClass":"sysml:ReferenceUsage","data":{"elementId":"9e08b023-69df-4262-92be-ce2b9c6339a0","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}},{"id":"560d42e6-c398-48cf-9816-1bd9cae87041","eClass":"sysml:FeatureMembership","data":{"elementId":"4b970e61-3c84-4cf2-9c1f-ea3ef53bb346","ownedRelatedElement":[{"id":"630f2641-b66a-4f52-91c6-057b1c5811d0","eClass":"sysml:StateUsage","data":{"declaredName":"normal","elementId":"59276fa3-50b8-45da-a7d0-1775b3632476","isComposite":true}}]}},{"id":"e1a46bc4-adad-4ecd-ac1f-ec293b4466da","eClass":"sysml:FeatureMembership","data":{"elementId":"ca2e7bf0-9cee-4679-b29c-60eeadc8f7c1","ownedRelatedElement":[{"id":"9390334a-f844-4352-91a5-f0fbcfa072f7","eClass":"sysml:StateUsage","data":{"declaredName":"heating","elementId":"502e583f-266e-4e03-82d5-9198951b9736","isComposite":true}}]}},{"id":"4486dadb-f285-43a9-b554-b5dbe1f30ec4","eClass":"sysml:FeatureMembership","data":{"elementId":"a87c855f-cbc1-441a-90f9-59427af39f5c","ownedRelatedElement":[{"id":"1ff2edd1-7ecc-4711-8e0b-1d9e6ba18afd","eClass":"sysml:StateUsage","data":{"declaredName":"cooling","elementId":"336fbd96-f982-4f5f-ba7b-611ca08daa02","isComposite":true}}]}},{"id":"b7c0db78-1dfc-4f68-aa58-72a367032fca","eClass":"sysml:FeatureMembership","data":{"elementId":"5027456b-92a5-468a-8dc3-4da4d77356e5","ownedRelatedElement":[{"id":"8e205929-279c-421f-b739-c4d498c114da","eClass":"sysml:AttributeUsage","data":{"declaredName":"currentTemp","elementId":"5e7f955d-d275-4a9f-a4f6-fd91e00c903a","ownedRelationship":[{"id":"5be10aa2-0fcf-4b76-81bc-c700d3889d97","eClass":"sysml:FeatureValue","data":{"elementId":"f590f069-37c2-4b72-b412-0c004e7b3969","ownedRelatedElement":[{"id":"d0dec34a-5aca-420e-b7d6-4530d7ca54b2","eClass":"sysml:LiteralRational","data":{"elementId":"22502ad2-6755-4a78-b001-ae7d823d7a1a","value":20.0}}],"isInitial":true}},{"id":"7f7aafa0-3fc2-4cb5-9aa0-81039d5d4a51","eClass":"sysml:FeatureTyping","data":{"elementId":"fdb43bab-e1cc-4c65-966d-8242a09d6c52","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"8e205929-279c-421f-b739-c4d498c114da"}}],"isComposite":true}}]}},{"id":"a6e0b47d-eb19-4328-b6d6-99e68bfed356","eClass":"sysml:FeatureMembership","data":{"elementId":"9fc5a32e-ece4-4122-bed7-51fc54c19ba1","ownedRelatedElement":[{"id":"541bd9c9-44fa-4440-bebc-3f84820674e8","eClass":"sysml:AttributeUsage","data":{"declaredName":"targetTemp","elementId":"2a8602b9-d12d-40a2-9ca4-eeb421a9e2ed","ownedRelationship":[{"id":"e375b8ff-ef56-407b-8538-52a31352d147","eClass":"sysml:FeatureValue","data":{"elementId":"b63a0860-3159-44cc-922e-e4e962e48d91","ownedRelatedElement":[{"id":"2c01bf77-f6e4-4677-92c3-945189119f2e","eClass":"sysml:LiteralRational","data":{"elementId":"1d6f8b13-f67a-4508-9514-0fdd42ce7a11","value":22.0}}]}},{"id":"de665ea7-84c3-4e42-9a77-f593fe17b134","eClass":"sysml:FeatureTyping","data":{"elementId":"46004252-bb31-4407-9d4d-1e02857d777e","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"541bd9c9-44fa-4440-bebc-3f84820674e8"}}],"isComposite":true}}]}},{"id":"8d07b2dd-cb18-4fc0-baa8-f651b807b46c","eClass":"sysml:FeatureMembership","data":{"elementId":"485d35c7-55b7-4e7c-807b-0c65402c854c","ownedRelatedElement":[{"id":"9e405dc0-36d0-47f8-8b8d-461ab5a31169","eClass":"sysml:AttributeUsage","data":{"declaredName":"tolerance","elementId":"c79fd2f6-b87e-45ab-a978-d6e365d1892b","ownedRelationship":[{"id":"30f1fde2-4f4b-4257-8a9d-851f11cc8e6f","eClass":"sysml:FeatureValue","data":{"elementId":"e8a2a7c8-3eb0-4eca-af46-8812cde65a6a","ownedRelatedElement":[{"id":"ae8ef32c-b7d9-4f25-992d-8abc484db859","eClass":"sysml:LiteralRational","data":{"elementId":"ea074070-969d-4a48-b05b-afed056dab84","value":1.0}}]}},{"id":"d4ff4acb-0756-4348-8f5a-ff6f5a4fed41","eClass":"sysml:FeatureTyping","data":{"elementId":"50c766d5-c266-474b-9c43-d2cc1c0c498d","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#14c0aa22-5489-59b5-b438-ded26e83ba31","typedFeature":"9e405dc0-36d0-47f8-8b8d-461ab5a31169"}}],"isComposite":true}}]}},{"id":"7656692f-d1a8-4b63-9969-0191adc8273e","eClass":"sysml:FeatureMembership","data":{"elementId":"d8e1cc5d-5bfb-4195-91db-318eeb0b2706","ownedRelatedElement":[{"id":"0b517687-ef3f-4057-9ce5-27e27f627a22","eClass":"sysml:TransitionUsage","data":{"declaredName":"to_heating","elementId":"722d9bb6-f15a-4133-9baa-5d87384af8b2","ownedRelationship":[{"id":"ad20395d-9921-44c2-8311-8afdcdf04157","eClass":"sysml:Membership","data":{"elementId":"1d757d16-ec70-4b47-a753-7dd9402e02bc","memberElement":"630f2641-b66a-4f52-91c6-057b1c5811d0"}},{"id":"e361c51a-2480-42e1-a181-ce7d079b2e93","eClass":"sysml:OwningMembership","data":{"elementId":"c2b19814-61f6-4e6e-938f-9cf8dd68ef62","ownedRelatedElement":[{"id":"8085cacc-ccab-4176-91bc-f90d138bf7f3","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"25f126b9-bc1f-43b5-8f65-6c11f41c569b","ownedRelationship":[{"id":"bc9dc7b9-9b80-4c4d-80e8-b137d62abf73","eClass":"sysml:EndFeatureMembership","data":{"elementId":"99db0664-a0b4-4ea0-87eb-cb801838d5fe","ownedRelatedElement":[{"id":"93e43e08-43e2-4c01-b101-436fefc0feb7","eClass":"sysml:ReferenceUsage","data":{"elementId":"430bfb97-e843-4c1d-9351-ed828370dcbc","isEnd":true}}]}},{"id":"45680a1a-a4e5-4f8b-933a-2f87141422ce","eClass":"sysml:EndFeatureMembership","data":{"elementId":"cdb2a753-f470-47a3-b073-f980d304fb23","ownedRelatedElement":[{"id":"7fd10dad-a085-472c-b06d-3173f81373b0","eClass":"sysml:ReferenceUsage","data":{"elementId":"28801261-d808-4b8b-a9c4-849aa076620f","ownedRelationship":[{"id":"ca8fd8cd-1fd0-4936-a7e8-474780da6295","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"6bf78264-9a29-4a7d-9f58-af4519b5a245","subsettingFeature":"7fd10dad-a085-472c-b06d-3173f81373b0","referencedFeature":"9390334a-f844-4352-91a5-f0fbcfa072f7"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"50ce6fd3-4386-43e7-9c79-92791f484058","eClass":"sysml:ParameterMembership","data":{"elementId":"4b1fa532-9975-475f-b7a3-2b8dccde5efa","ownedRelatedElement":[{"id":"a50dc602-495e-4cbf-8587-a7909d1e064d","eClass":"sysml:ReferenceUsage","data":{"elementId":"918f482a-795f-4158-9f1b-16f94c2cfb4a","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}},{"id":"211a29e1-2104-4473-bc7a-e7675162e447","eClass":"sysml:FeatureMembership","data":{"elementId":"30842099-ac84-4baf-aae8-d78c11ba491a","ownedRelatedElement":[{"id":"e1c27d0b-476e-42d4-9b81-b5f064d9cbbe","eClass":"sysml:TransitionUsage","data":{"declaredName":"to_cooling","elementId":"3df50466-18d3-48d1-9b1a-6b35ab79f2b7","ownedRelationship":[{"id":"6971555b-4116-48f0-9847-9292f1357d54","eClass":"sysml:Membership","data":{"elementId":"1ae63504-5879-44d8-845c-58145c66cee8","memberElement":"630f2641-b66a-4f52-91c6-057b1c5811d0"}},{"id":"2f3331f5-bbe1-44c4-844b-d24c8f2eeffb","eClass":"sysml:TransitionFeatureMembership","data":{"elementId":"f0e1ee9a-8235-4875-b5e3-1ff7f8ac8a85","ownedRelatedElement":[{"id":"ec67ead5-7739-4dd3-a82e-f1bb5ef34268","eClass":"sysml:OperatorExpression","data":{"elementId":"974ce809-d343-4a2f-8fe5-cec41eb46ba4","ownedRelationship":[{"id":"371effd8-5eea-4802-94e5-704498a68b13","eClass":"sysml:ParameterMembership","data":{"elementId":"bc3ab140-50b6-4c33-9874-23ebc399610c","ownedRelatedElement":[{"id":"40353cdf-d10b-4322-b623-05972ba837e3","eClass":"sysml:Feature","data":{"elementId":"664143da-4921-42a1-9cfd-cfc893719859","ownedRelationship":[{"id":"1eea43cc-f48e-4dc3-bf5d-574dd7fadcb9","eClass":"sysml:FeatureValue","data":{"elementId":"4f6f4e36-88e9-4ce7-b53f-2bb46878a9f3","ownedRelatedElement":[{"id":"1440ba96-39f9-4745-9596-625baab83480","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"a06418d2-453e-4ea7-90c6-cedb31a54a93","ownedRelationship":[{"id":"3af419a1-d8cd-4f4e-a95f-03d88df310c5","eClass":"sysml:Membership","data":{"elementId":"94b074ff-c911-4b46-838c-d617bc424d2d","memberElement":"8e205929-279c-421f-b739-c4d498c114da"}}]}}]}}],"direction":"in"}}]}},{"id":"b6bbf397-915c-4df2-8153-046eddf48b60","eClass":"sysml:ParameterMembership","data":{"elementId":"1c049222-6ff6-4835-a00a-8df9349d8266","ownedRelatedElement":[{"id":"e73a1795-d083-4539-953e-6ce302c9c7a0","eClass":"sysml:Feature","data":{"elementId":"4b579ae4-8c79-4c88-9b21-db059d7857b7","ownedRelationship":[{"id":"74a4b0ca-269e-480b-b209-5a36bcfcad18","eClass":"sysml:FeatureValue","data":{"elementId":"5a8e236c-908c-478f-94a4-1dccce775e9f","ownedRelatedElement":[{"id":"c656679c-b3f8-4218-8b7c-94f40c432e9e","eClass":"sysml:OperatorExpression","data":{"elementId":"e7eac6ca-3967-41af-8f50-de765b9e7c87","ownedRelationship":[{"id":"90b4fda8-1e61-4157-966d-60ebaa3cecaa","eClass":"sysml:ParameterMembership","data":{"elementId":"8dfcdc00-48ae-4f73-b096-74263b2ff799","ownedRelatedElement":[{"id":"c612b8d5-4885-4c2e-85ed-6d0ad2074202","eClass":"sysml:Feature","data":{"elementId":"c1768702-c8ce-41c2-8c51-8195d64c97b6","ownedRelationship":[{"id":"03915a4e-b0da-48e8-863f-30f53ac37cea","eClass":"sysml:FeatureValue","data":{"elementId":"b00bb56f-0b71-407c-baa4-90956eb99b76","ownedRelatedElement":[{"id":"13070906-e608-4e8d-8418-3fc3916128f0","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"882ab32c-e241-4989-955c-608a9acbae32","ownedRelationship":[{"id":"9726f328-2238-44c6-b640-29ebad59d238","eClass":"sysml:Membership","data":{"elementId":"bc37097c-d761-4b2d-b506-9c807f25c907","memberElement":"541bd9c9-44fa-4440-bebc-3f84820674e8"}}]}}]}}],"direction":"in"}}]}},{"id":"3d67e9aa-3fc0-4e48-a7a0-632f6b5730c1","eClass":"sysml:ParameterMembership","data":{"elementId":"a56ba801-d9c5-4302-99ae-d5e68376378c","ownedRelatedElement":[{"id":"870fc619-a861-4d71-8b0d-4c700c8c4b3f","eClass":"sysml:Feature","data":{"elementId":"bd432a49-148b-4805-b314-6aaa2b485eba","ownedRelationship":[{"id":"db0aabd1-5712-4faa-86b7-4bd4176b4389","eClass":"sysml:FeatureValue","data":{"elementId":"9702fb41-ed29-4393-bbba-8c6b864eb4e0","ownedRelatedElement":[{"id":"edc353c4-b66d-4b2b-b8d5-55f8246922ab","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"82a521eb-14f5-4e56-be04-49bb2a83678d","ownedRelationship":[{"id":"7e2b0f6f-6c10-4670-aa85-2294aadb7be3","eClass":"sysml:Membership","data":{"elementId":"225dfdb5-3b07-4afd-bb2d-554318c82ed5","memberElement":"9e405dc0-36d0-47f8-8b8d-461ab5a31169"}}]}}]}}],"direction":"in"}}]}}],"operator":"+"}}]}}],"direction":"in"}}]}}],"operator":">"}}],"kind":"guard"}},{"id":"f2dd1298-dec2-44c6-bccc-c6aff0942073","eClass":"sysml:OwningMembership","data":{"elementId":"c405c6cb-696d-4a3f-99e7-6dab852ae228","ownedRelatedElement":[{"id":"c4e5cb98-07df-49a6-a3b1-93e9d9d6f4f5","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"acd50047-bcc8-421f-b204-cef2c540355e","ownedRelationship":[{"id":"73144371-94ed-44bb-95ab-70a1a23360fe","eClass":"sysml:EndFeatureMembership","data":{"elementId":"697afe9a-c1f3-4c65-a063-7bc9ab4b106d","ownedRelatedElement":[{"id":"220c6106-c36f-4497-88e2-42125a9f69d5","eClass":"sysml:ReferenceUsage","data":{"elementId":"b1438ef2-513a-43b5-a292-7e34cd24f941","isEnd":true}}]}},{"id":"39c31f59-67df-408c-bf92-9b1c9dc209df","eClass":"sysml:EndFeatureMembership","data":{"elementId":"7e270dc1-76ee-4023-9d55-128523dd8d26","ownedRelatedElement":[{"id":"9dd8948f-9a7c-4682-bc46-dda6eaf7eed8","eClass":"sysml:ReferenceUsage","data":{"elementId":"8a705bce-88f0-4558-b010-cac84b264413","ownedRelationship":[{"id":"16928b68-0b7c-4da8-891f-bf6fc174361a","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"36718153-4eb3-48ca-8023-b1ca8a606897","subsettingFeature":"9dd8948f-9a7c-4682-bc46-dda6eaf7eed8","referencedFeature":"1ff2edd1-7ecc-4711-8e0b-1d9e6ba18afd"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"6b679136-5121-49fc-b63f-edea84f72f73","eClass":"sysml:ParameterMembership","data":{"elementId":"9d7e2288-1a5a-4ad9-aa04-96f72ddca831","ownedRelatedElement":[{"id":"5d11e636-89f5-4d42-a233-207b6d62d418","eClass":"sysml:ReferenceUsage","data":{"elementId":"8ffbe3d2-f907-4fd2-b2b3-c67ac8ef75bd","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}},{"id":"da3ff9dc-93bb-406a-a2cb-6bb17b551a62","eClass":"sysml:FeatureMembership","data":{"elementId":"6cf8e042-d8f9-41b6-9a1d-3232943b2b14","ownedRelatedElement":[{"id":"c7efc3c8-830f-41f4-a9cb-a3667f7c7637","eClass":"sysml:TransitionUsage","data":{"declaredName":"heating_done","elementId":"6bb2e424-424c-44d4-afb6-3923e8202303","ownedRelationship":[{"id":"c2f144d8-86b6-4dd7-b2ff-7280703d9e5d","eClass":"sysml:Membership","data":{"elementId":"551669f0-c179-4998-8de3-84a03f2255b4","memberElement":"9390334a-f844-4352-91a5-f0fbcfa072f7"}},{"id":"6dac8578-e793-4cc2-9581-59c2ef40532c","eClass":"sysml:TransitionFeatureMembership","data":{"elementId":"cf32b8b1-18ec-4787-9eb0-95da2eeb675a","ownedRelatedElement":[{"id":"e9f31b7a-9673-4037-859b-7d6e5775db53","eClass":"sysml:OperatorExpression","data":{"elementId":"514158d0-9ec0-4ca8-8cf6-c201eded035b","ownedRelationship":[{"id":"2b0b4608-771a-4b49-8264-ee9022d5f533","eClass":"sysml:ParameterMembership","data":{"elementId":"c32a9d2f-ed2f-442c-9eef-7f282e73f828","ownedRelatedElement":[{"id":"f9b860f0-3ab9-4b35-a5af-7b87c85aa51a","eClass":"sysml:Feature","data":{"elementId":"be79d529-4b62-4620-96f6-151347ef888a","ownedRelationship":[{"id":"06a57d10-f450-4edc-8d6d-7e9c085d5678","eClass":"sysml:FeatureValue","data":{"elementId":"b8b2fe56-0ab6-41c9-ab2a-fc9f238d4c36","ownedRelatedElement":[{"id":"07ecaa6c-9469-4602-9938-ef4fe35c8e61","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"498a0218-a6a1-4d1d-951d-b9f2fb76a93a","ownedRelationship":[{"id":"2a962769-9796-46c5-a084-e78784f6e775","eClass":"sysml:Membership","data":{"elementId":"4cf777d6-945d-407c-a1ed-e0b1af63fe54","memberElement":"8e205929-279c-421f-b739-c4d498c114da"}}]}}]}}],"direction":"in"}}]}},{"id":"925a2b9f-4c32-49a1-9b83-23b44f262ae8","eClass":"sysml:ParameterMembership","data":{"elementId":"2b1b4a8e-8d3e-4611-9e2b-e67fb634a03c","ownedRelatedElement":[{"id":"2a0e96be-a356-424d-a497-1fb6e0a67a75","eClass":"sysml:Feature","data":{"elementId":"b118b738-974f-47c8-9000-e676ff11acf9","ownedRelationship":[{"id":"77fa7bc6-7751-48fd-b4db-811e8ddb0107","eClass":"sysml:FeatureValue","data":{"elementId":"0faa3816-569c-44c2-91f4-0a62629bdc35","ownedRelatedElement":[{"id":"8deba717-e9e8-4359-a7ee-0392cc60da28","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"4f0cbbb1-ed40-47c5-9fdc-716954416eca","ownedRelationship":[{"id":"31c29b60-785d-4c12-91ff-1a6bd5cd0a97","eClass":"sysml:Membership","data":{"elementId":"cfb554e6-b753-40aa-8212-646ac2307d97","memberElement":"541bd9c9-44fa-4440-bebc-3f84820674e8"}}]}}]}}],"direction":"in"}}]}}],"operator":">="}}],"kind":"guard"}},{"id":"52bd3814-e962-4bf3-bdbc-af103344c0ad","eClass":"sysml:OwningMembership","data":{"elementId":"68dca418-736b-429d-b442-271730f62bed","ownedRelatedElement":[{"id":"bf42847e-36cd-4d3d-aa5f-adbda8deedf1","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"1235177c-931e-444e-841a-74a0033b7573","ownedRelationship":[{"id":"71fc2ef3-58ee-46f5-9f59-16782d9d18ca","eClass":"sysml:EndFeatureMembership","data":{"elementId":"64387b5f-6f0c-43c6-8630-9f507376e5e3","ownedRelatedElement":[{"id":"445af89b-6f69-4a1b-bb66-ee2fad4012bd","eClass":"sysml:ReferenceUsage","data":{"elementId":"39473346-c45b-4bf0-a667-771e453ca497","isEnd":true}}]}},{"id":"853b2327-fd95-4281-92d3-39675c9832ff","eClass":"sysml:EndFeatureMembership","data":{"elementId":"ae636aba-2171-48b4-9bc9-36b8e319a679","ownedRelatedElement":[{"id":"b2b75688-a923-4a1f-966d-bacdff99b033","eClass":"sysml:ReferenceUsage","data":{"elementId":"a4debe8e-e4c5-4e03-ac70-94f40d27f9e9","ownedRelationship":[{"id":"43ce1363-6e62-4981-b36e-b8e0b32431f3","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"66ec302b-b8c2-4029-8536-830eeb9904ae","subsettingFeature":"b2b75688-a923-4a1f-966d-bacdff99b033","referencedFeature":"630f2641-b66a-4f52-91c6-057b1c5811d0"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"a4f9b12e-4854-450c-a5b4-c13417654040","eClass":"sysml:ParameterMembership","data":{"elementId":"46d97c31-de66-482e-91da-e7fbc6555f7a","ownedRelatedElement":[{"id":"f7e4f511-9110-424d-9787-c4bec859f3bb","eClass":"sysml:ReferenceUsage","data":{"elementId":"e1cdc0e3-a709-4173-9d58-7500c52b4d9f","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}},{"id":"fd55211f-bc7f-4598-8bc1-0a3d4b54f974","eClass":"sysml:FeatureMembership","data":{"elementId":"888f6364-e8aa-4312-8477-baa01db1b493","ownedRelatedElement":[{"id":"8a153941-bf0a-4010-a51e-62acb8bd63bb","eClass":"sysml:TransitionUsage","data":{"declaredName":"cooling_done","elementId":"9bb3b3d0-59ae-43ea-aeeb-27cd95233987","ownedRelationship":[{"id":"5a798e86-8ac9-428c-bd43-983a008a96ce","eClass":"sysml:Membership","data":{"elementId":"150daea0-ca87-4737-b1cc-be68c0708d45","memberElement":"1ff2edd1-7ecc-4711-8e0b-1d9e6ba18afd"}},{"id":"9a4869bf-59f1-40fe-bb41-5b80125eec95","eClass":"sysml:TransitionFeatureMembership","data":{"elementId":"0ee132d8-b870-40a6-93b2-0e386ca698ec","ownedRelatedElement":[{"id":"506f2589-c9aa-497d-b9e2-9f3568a1b60e","eClass":"sysml:OperatorExpression","data":{"elementId":"b17dcb88-0404-4a2d-b175-29b5c2430b10","ownedRelationship":[{"id":"9439080c-7bc5-442c-a234-4fbced1722fd","eClass":"sysml:ParameterMembership","data":{"elementId":"03eeff23-1ce5-40e9-b8f8-8c971d8fea9b","ownedRelatedElement":[{"id":"4bb65cfe-8311-4ac1-85d8-1b19421553b0","eClass":"sysml:Feature","data":{"elementId":"12652e9b-2669-4e27-90fb-3d6c3f3a665b","ownedRelationship":[{"id":"6ec15f89-da66-4dc9-bdf1-34a8235a0e60","eClass":"sysml:FeatureValue","data":{"elementId":"516566a3-89e8-4057-8bb4-3238a772efae","ownedRelatedElement":[{"id":"131bcb2c-6bd2-4e48-a392-4bc83d15ec5d","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"71069a61-0947-49ae-82a6-131855801506","ownedRelationship":[{"id":"3b4c7e1b-00ad-4ef4-8f12-87c642d7e340","eClass":"sysml:Membership","data":{"elementId":"b7da76e8-90dd-4816-a096-3bc4d74fcfff","memberElement":"8e205929-279c-421f-b739-c4d498c114da"}}]}}]}}],"direction":"in"}}]}},{"id":"4c107a4c-9209-412b-953e-60b1c643382a","eClass":"sysml:ParameterMembership","data":{"elementId":"4e92e729-b2d1-424c-9471-8f8c17cdc6b7","ownedRelatedElement":[{"id":"010203af-dce4-454e-a19a-888ae9c3b675","eClass":"sysml:Feature","data":{"elementId":"401748ba-f2fe-4fd0-903a-bbbfb3067510","ownedRelationship":[{"id":"4ba22430-f1f1-4462-a196-ed8b6e119841","eClass":"sysml:FeatureValue","data":{"elementId":"9854172d-ee15-4e79-9acd-9717b3b5c598","ownedRelatedElement":[{"id":"61a15a33-8067-4d16-a44f-73bf1eb99e2d","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"972b0f23-2c30-42c6-93d4-71b2d291460e","ownedRelationship":[{"id":"364f5f78-67b1-4631-9d0e-01641d95afc9","eClass":"sysml:Membership","data":{"elementId":"cfba9064-3b74-4bf4-bdbf-97e54c8b2013","memberElement":"541bd9c9-44fa-4440-bebc-3f84820674e8"}}]}}]}}],"direction":"in"}}]}}],"operator":"<="}}],"kind":"guard"}},{"id":"0f19e6b9-2973-411c-b800-8d0ed49795be","eClass":"sysml:OwningMembership","data":{"elementId":"d2755907-2b2e-452f-8051-637f99928b24","ownedRelatedElement":[{"id":"d8aee92a-f512-4762-b4e4-b948f450a024","eClass":"sysml:SuccessionAsUsage","data":{"elementId":"c1cb5168-b133-4e08-8f9f-3243ee80134d","ownedRelationship":[{"id":"9b1ee916-7707-43a1-937c-7cad3d9b5b77","eClass":"sysml:EndFeatureMembership","data":{"elementId":"1526df00-9a9b-41e4-8224-e54e782c429d","ownedRelatedElement":[{"id":"03e69081-de75-4755-8a05-056340bfcf21","eClass":"sysml:ReferenceUsage","data":{"elementId":"b907c9b6-3433-4113-9ed8-34644ca93a8c","isEnd":true}}]}},{"id":"f8423145-7a38-4e0b-848d-67a70db029c6","eClass":"sysml:EndFeatureMembership","data":{"elementId":"2595cd38-81ee-46c6-b87c-7c948a5ab166","ownedRelatedElement":[{"id":"98dfa1ca-9866-4079-b43d-b61af3c53e64","eClass":"sysml:ReferenceUsage","data":{"elementId":"ada1671c-beb1-4333-b144-1c9568840b05","ownedRelationship":[{"id":"629647f9-2a93-493e-883d-d5c0cb6d9fdd","eClass":"sysml:ReferenceSubsetting","data":{"elementId":"de396f89-7470-469c-9ab6-a726f8de8400","subsettingFeature":"98dfa1ca-9866-4079-b43d-b61af3c53e64","referencedFeature":"630f2641-b66a-4f52-91c6-057b1c5811d0"}}],"isComposite":true,"isEnd":true}}]}}],"isComposite":true}}]}},{"id":"5ed10d2c-ac2b-4864-9b4f-74218a8119d7","eClass":"sysml:ParameterMembership","data":{"elementId":"8444ae28-737f-4667-b584-a5ec3afd4cc5","ownedRelatedElement":[{"id":"3414baf1-cc30-4843-90e6-ed004d4b11f4","eClass":"sysml:ReferenceUsage","data":{"elementId":"a9f3e3d7-8086-467c-8a9f-040ae5ec8f4a","direction":"in","isComposite":true}}]}}],"isComposite":true}}]}}]}}]}},{"id":"f1ac0d22-7a0f-413b-b576-85a3ffbe6135","eClass":"sysml:OwningMembership","data":{"elementId":"e648c25f-e278-4e65-9133-0dc96e1f0026","ownedRelatedElement":[{"id":"69d595d7-6a07-4cb1-a4ea-e6102b4ec12d","eClass":"sysml:ConcernDefinition","data":{"declaredName":"SafetyAndReliability","elementId":"3c2ae026-8af6-4bf4-bc7c-79234a98eff9","ownedRelationship":[{"id":"ef1499c9-c7f7-4acb-acf5-e8877c9383c8","eClass":"sysml:FeatureMembership","data":{"elementId":"c610ab68-ac78-4599-b05e-7174225eeaa7","ownedRelatedElement":[{"id":"e2678e9c-3155-4570-bd56-4c8457084f05","eClass":"sysml:AttributeUsage","data":{"declaredName":"deploymentCount","elementId":"9c8fd285-2a30-4ee8-9e2b-cc5a38f03e85","ownedRelationship":[{"id":"3e8dbff8-89e0-4113-a368-9080e6e59b45","eClass":"sysml:FeatureValue","data":{"elementId":"7359a844-4139-4401-b296-a9ab08f92acf","ownedRelatedElement":[{"id":"2db9e19e-7b70-420f-bbab-875dd36ee56f","eClass":"sysml:LiteralInteger","data":{"elementId":"05d4dfe9-b7c6-4b85-864b-83f347eee7be","value":3}}]}},{"id":"21d2afe7-e7b0-4994-9dca-09221ba31bf1","eClass":"sysml:FeatureTyping","data":{"elementId":"c6132f83-494c-4a60-bbac-1862a5261fc7","type":"sysml:DataType kermllibrary:///b2c6dd37-2084-3ce4-9ce2-580fdf30629c#f2350199-2ab1-5258-8514-58812ef25dc6","typedFeature":"e2678e9c-3155-4570-bd56-4c8457084f05"}}],"isComposite":true}}]}},{"id":"5878dce7-7b5a-4f3f-9780-b01ccdd0c379","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"d788cb4d-7166-4e73-ac86-425afa925dec","ownedRelatedElement":[{"id":"5e36a9cd-89d3-4653-b767-0e1be2d32ad5","eClass":"sysml:ConstraintUsage","data":{"elementId":"585f8cab-50a5-47a5-8ea1-3f5929be4af2","ownedRelationship":[{"id":"821531fa-1c3b-468e-aeba-5f2f8ce6e2a4","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"a5488ef2-0227-4422-ac46-dda945fcbf4d","ownedRelatedElement":[{"id":"8a6c830c-98af-403f-a4de-938a31f61194","eClass":"sysml:OperatorExpression","data":{"elementId":"8f0efd41-c032-4a95-b4da-80641f15fa5a","ownedRelationship":[{"id":"298ac857-1424-421a-8895-f8b2ef5cfc55","eClass":"sysml:ParameterMembership","data":{"elementId":"337a51c2-5cb0-4c94-9309-06d838bdb875","ownedRelatedElement":[{"id":"b0ce01ae-8dd1-406b-b61e-6b5c0200a4bc","eClass":"sysml:Feature","data":{"elementId":"bed903ba-e17d-426e-bef1-3c83fed8ad5b","ownedRelationship":[{"id":"1489f1a6-db01-4f94-b7ba-3c221451fbbe","eClass":"sysml:FeatureValue","data":{"elementId":"294d19aa-e1a2-429a-a91e-35b068b48631","ownedRelatedElement":[{"id":"9e0eed6b-d20e-449a-b674-3e54e3d53859","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"4affe832-9f21-4775-abf7-9e33ea8c6c8e","ownedRelationship":[{"id":"37daf8ad-f0b9-4ac1-96f1-29da588d1fe5","eClass":"sysml:Membership","data":{"elementId":"bf9614cb-198c-4f53-9f63-0f1aa8820c10","memberElement":"e2678e9c-3155-4570-bd56-4c8457084f05"}}]}}]}}],"direction":"in"}}]}},{"id":"f35b3dda-8d34-4dc3-94e0-e5f7a7accff9","eClass":"sysml:ParameterMembership","data":{"elementId":"2bd75866-58d5-4522-aca1-6cf4196ed16b","ownedRelatedElement":[{"id":"1eace1ad-8813-4660-a1cd-332909552774","eClass":"sysml:Feature","data":{"elementId":"bd79950f-6dbf-4c42-8f2c-06aa4b7744c6","ownedRelationship":[{"id":"64d75861-f3c8-44f3-a0b8-dbf2e7878dc3","eClass":"sysml:FeatureValue","data":{"elementId":"07085d12-61df-40fa-8768-49c55c23e67b","ownedRelatedElement":[{"id":"56c8f7f8-93ef-4666-aa56-53b6531c0d4f","eClass":"sysml:LiteralInteger","data":{"elementId":"1791dfbd-3e30-488c-bbbe-30816bad15bc"}}]}}],"direction":"in"}}]}}],"operator":">"}}]}}],"isComposite":true}}]}},{"id":"597a0e23-3eef-471c-8dbd-75562a4e4d7a","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"b0aef8c1-d38c-4bd9-8ae2-4f0542fe91d7","ownedRelatedElement":[{"id":"b34a95e4-cb32-4a1c-b1ee-17d4bd62c7d2","eClass":"sysml:ConstraintUsage","data":{"declaredName":"minimumRedundancy","elementId":"76a7a9b1-9ecc-42b1-9a41-5b328053f12f","ownedRelationship":[{"id":"8c80e3db-82fa-41c4-be21-274b737f7284","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"dbb57839-6678-448a-8221-be6911b0f848","ownedRelatedElement":[{"id":"ed00c629-0f82-4874-9590-5a775e35081d","eClass":"sysml:OperatorExpression","data":{"elementId":"c67dae08-fad7-409c-ae7b-ee013cca30d6","ownedRelationship":[{"id":"7d801324-bad9-46b1-970f-7a2b38a556aa","eClass":"sysml:ParameterMembership","data":{"elementId":"8a478841-dab9-42a1-8966-ed68ec3be498","ownedRelatedElement":[{"id":"dc503057-d95b-4569-b694-9c03d8a4a711","eClass":"sysml:Feature","data":{"elementId":"5ee82585-9fb8-4395-8b39-e9ffc2ac6d80","ownedRelationship":[{"id":"8c469bc4-c47f-4ea8-9b6d-2aa4c643d7c1","eClass":"sysml:FeatureValue","data":{"elementId":"e1671e98-bc6d-4194-84c8-6f79c29850c0","ownedRelatedElement":[{"id":"2d5735e7-8a41-4fd8-856e-46af848a86f0","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"7f2be62b-42dc-4161-bbf1-9e33a596e6f7","ownedRelationship":[{"id":"cf8e2ed0-00d1-4e87-8a2f-b23d7c6c2879","eClass":"sysml:Membership","data":{"elementId":"f11aecf9-0c21-45a8-843f-fe5d4d96a0a9","memberElement":"e2678e9c-3155-4570-bd56-4c8457084f05"}}]}}]}}],"direction":"in"}}]}},{"id":"6f077a43-6f45-49b1-9232-b3282f0abb57","eClass":"sysml:ParameterMembership","data":{"elementId":"c2c6e7b8-af06-4096-989a-3c2e95baca51","ownedRelatedElement":[{"id":"0309d85e-37cc-499f-b876-846b2d5a4c2a","eClass":"sysml:Feature","data":{"elementId":"ca1bdcc4-dadc-4228-8d61-813b74f39cc7","ownedRelationship":[{"id":"dfad1497-873d-4629-a277-c254e1a247a9","eClass":"sysml:FeatureValue","data":{"elementId":"a50bbd08-8668-4de4-a64f-016da901a339","ownedRelatedElement":[{"id":"d1b9e2c7-f50f-44a3-a585-7f531eb3fbf9","eClass":"sysml:LiteralInteger","data":{"elementId":"48a9725a-a225-454e-8421-a03542e9e7e8","value":2}}]}}],"direction":"in"}}]}}],"operator":">="}}]}}],"isComposite":true}}],"kind":"requirement"}}]}}]}},{"id":"a6f07c1f-b22d-4081-a3d9-3fc78dee4d22","eClass":"sysml:OwningMembership","data":{"elementId":"db5e69df-ce41-43a4-a9c1-2655173fae74","ownedRelatedElement":[{"id":"da2296a4-25ed-4f04-abc8-d47f93cd223c","eClass":"sysml:ConcernDefinition","data":{"declaredName":"PerformanceConcern","elementId":"719defb0-780c-486b-85d5-b3d4e5b460cc","ownedRelationship":[{"id":"d1836f12-7281-4b5b-a0d2-26cfef749277","eClass":"sysml:SubjectMembership","data":{"elementId":"35bf0d61-c429-43fb-8f17-f6059aa0c4d3","ownedRelatedElement":[{"id":"33466a30-d081-4d0d-a3dc-142c59b82000","eClass":"sysml:ReferenceUsage","data":{"declaredName":"s","elementId":"ea6f2066-3a2f-4888-9eb8-33e6cec61b8d","ownedRelationship":[{"id":"1355d4f4-a4c7-4a0c-bb4a-a8dd29b2c3df","eClass":"sysml:FeatureTyping","data":{"elementId":"caea1502-7778-408f-ac2b-db317a07154f","type":"3f2124ff-5c56-486b-a296-b1c3476e5180","typedFeature":"33466a30-d081-4d0d-a3dc-142c59b82000"}}],"direction":"in","isComposite":true}}]}},{"id":"a4eda0c9-d3e4-4ebc-8d8b-0c864423acf4","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"fbab8f7d-eaf7-4510-bed3-2679d739229a","ownedRelatedElement":[{"id":"4fd2c402-15b7-4546-ad9c-7aa9419a1528","eClass":"sysml:ConstraintUsage","data":{"elementId":"98f1df4b-ff5c-498a-960b-9cb66ef0dbf9","isComposite":true}}]}},{"id":"934e8eac-2e71-4b04-b759-d928aee33336","eClass":"sysml:RequirementConstraintMembership","data":{"elementId":"df1c9c38-1d8b-49e6-b8f9-4cdc9af5b9d9","ownedRelatedElement":[{"id":"6891b4f5-6f02-4c73-ae23-db88ad10b253","eClass":"sysml:ConstraintUsage","data":{"declaredName":"responsiveness","elementId":"c0f01f9c-e59f-4517-9d2c-a295ce86c4b2","ownedRelationship":[{"id":"d20bfa80-aa30-436f-a5bf-96e2217efa58","eClass":"sysml:ResultExpressionMembership","data":{"elementId":"33ea9c9a-383c-4d7e-b356-6df68a277de9","ownedRelatedElement":[{"id":"6f2e1644-b09d-46a2-b155-fc123e0f2953","eClass":"sysml:OperatorExpression","data":{"elementId":"378814e3-5c6b-4aac-983b-56d7ba918c1d","ownedRelationship":[{"id":"93a8bf8c-3c78-47f4-be7a-5bfe2faccaa3","eClass":"sysml:ParameterMembership","data":{"elementId":"8b7301cd-24b5-4b2b-8e1a-5a0dc096bdbc","ownedRelatedElement":[{"id":"fb7b2c32-7fe5-4ad7-bf3c-fc17280e4f4b","eClass":"sysml:Feature","data":{"elementId":"5a5849ab-2fa4-4966-8981-e252afcbe1f8","ownedRelationship":[{"id":"3a8347bc-70f7-4de8-892f-676542d63765","eClass":"sysml:FeatureValue","data":{"elementId":"dc48009e-02ff-4dd9-8986-71bef33e499f","ownedRelatedElement":[{"id":"501238ee-b170-4a71-add9-c3cc4b62b541","eClass":"sysml:OperatorExpression","data":{"elementId":"b4f2d2fc-f450-48f8-ade1-138bedd954ab","ownedRelationship":[{"id":"0e53a9bd-1bd5-4b12-931c-da6a744381be","eClass":"sysml:ParameterMembership","data":{"elementId":"2eecf130-286c-498a-b073-6ccd04e790dd","ownedRelatedElement":[{"id":"244833c3-2b41-467a-bb18-03bdca1aaf91","eClass":"sysml:Feature","data":{"elementId":"35c7ccb4-753a-4b3b-8bee-1e87f1faba9d","ownedRelationship":[{"id":"c2125fb7-4da1-4c18-ae2d-545b2ca6e3d3","eClass":"sysml:FeatureValue","data":{"elementId":"09b48893-6827-4cf9-bec6-88b807844c19","ownedRelatedElement":[{"id":"c19dca8a-1bed-49cd-a027-c10b6570e483","eClass":"sysml:FeatureChainExpression","data":{"elementId":"7a3c3fe4-404d-4889-9e34-1a3dd5ce5ca5","ownedRelationship":[{"id":"2febd014-6a70-48ac-9a28-211fb0cfc2bb","eClass":"sysml:ParameterMembership","data":{"elementId":"57bc8b4b-0a38-4b7f-b3a6-5d0523cc728c","ownedRelatedElement":[{"id":"a236b40b-3240-4162-8309-ba8de4a00b11","eClass":"sysml:Feature","data":{"elementId":"8a6e54a2-dd2a-46c2-840e-a0444dfc17d7","ownedRelationship":[{"id":"d4999cc0-72f8-418c-8a16-8d362577a450","eClass":"sysml:FeatureValue","data":{"elementId":"1371d3c0-f0db-4df2-b39a-86196bffcd6a","ownedRelatedElement":[{"id":"50e3db21-7e60-491c-ac6c-9a7dc9407207","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"4257cf6d-7f15-4fe9-b48e-24a072c6f040","ownedRelationship":[{"id":"87ec1ec4-0515-440b-b35c-8b8581217414","eClass":"sysml:Membership","data":{"elementId":"294b0f69-810d-4be2-bbdc-2022d8aa9794","memberElement":"33466a30-d081-4d0d-a3dc-142c59b82000"}}]}}]}}],"direction":"in"}}]}},{"id":"091ee316-159d-400d-94b8-ba0aa5c2f9b5","eClass":"sysml:Membership","data":{"elementId":"1dac782f-da3d-4fe4-9d47-3fa569692bcc","memberElement":"b922024b-5587-4be6-9924-6afa52ce8bf2"}}]}}]}}],"direction":"in"}}]}},{"id":"379e1891-786a-48ee-96a3-d6e3b00cde72","eClass":"sysml:ParameterMembership","data":{"elementId":"ef6e61be-424e-4063-bf2f-a81da5029fcc","ownedRelatedElement":[{"id":"fefc7b1f-cd73-4ef5-95bf-37f12b0749b8","eClass":"sysml:Feature","data":{"elementId":"d080d7d8-b587-4f09-8546-3b7515b73bf7","ownedRelationship":[{"id":"b08c94c0-5fd7-4498-81f8-91cc16df465e","eClass":"sysml:FeatureValue","data":{"elementId":"47f17548-693b-4b52-b6c5-146f93103b88","ownedRelatedElement":[{"id":"f8e0dcc5-6e48-4e37-a14d-37a1026ee01a","eClass":"sysml:LiteralRational","data":{"elementId":"39ac2b8c-4701-4616-8d73-6dc7ffe2c9cd","value":50.0}}]}}],"direction":"in"}}]}}],"operator":">="}}]}}],"direction":"in"}}]}},{"id":"eb8516c8-e8c8-4ee8-939a-55325c8b1cea","eClass":"sysml:ParameterMembership","data":{"elementId":"218a433e-cfa0-4902-9b4e-f0587be3b80e","ownedRelatedElement":[{"id":"c5b2f829-df1b-40ce-83d3-113b3bbfaba5","eClass":"sysml:Feature","data":{"elementId":"4134c6cb-8c93-4d25-af07-c284f7be3969","ownedRelationship":[{"id":"d60596d0-3c29-41a6-b460-898ed9b4c263","eClass":"sysml:FeatureValue","data":{"elementId":"7d1049c4-86cf-4589-b629-8d63970b8583","ownedRelatedElement":[{"id":"eb62764e-be00-4f53-a943-2bb9afe26d4c","eClass":"sysml:OperatorExpression","data":{"elementId":"00efad22-e113-41bc-be71-47de9a1fb9a8","ownedRelationship":[{"id":"64060226-0dc0-4dc1-8a5e-d01c98830213","eClass":"sysml:ParameterMembership","data":{"elementId":"300b058f-b25b-4281-90fe-684eb92606de","ownedRelatedElement":[{"id":"380808e7-4aa1-4f04-8296-8dbe5d1b9fc1","eClass":"sysml:Feature","data":{"elementId":"63ae1974-7814-4c0a-893e-72850334c386","ownedRelationship":[{"id":"23d8c3ac-ca81-45bb-956a-8e8597b49033","eClass":"sysml:FeatureValue","data":{"elementId":"8a290ed0-03f2-495d-bbc6-28f3570feb4b","ownedRelatedElement":[{"id":"4ad5fcc2-dcbe-4254-8e63-f4dc78552ad9","eClass":"sysml:OperatorExpression","data":{"elementId":"ff15a082-0091-4e71-bdf4-8cd81328f2ca","ownedRelationship":[{"id":"3949740d-1e51-4a76-81d9-66066d200c63","eClass":"sysml:ParameterMembership","data":{"elementId":"c63c1f9d-e626-47dc-9d32-59038df3b448","ownedRelatedElement":[{"id":"9a3ac2bf-cc5f-4854-87bc-14a035ad38e5","eClass":"sysml:Feature","data":{"elementId":"47d4f5d4-feb4-404c-9d39-e36cec21db8e","ownedRelationship":[{"id":"9d85853c-5921-4c83-a954-2d022e66f86d","eClass":"sysml:FeatureValue","data":{"elementId":"3f3a18b5-da04-4aa6-831c-984e8a65d47b","ownedRelatedElement":[{"id":"d3f88bd1-b325-4743-920b-e29c5806d7ef","eClass":"sysml:FeatureChainExpression","data":{"elementId":"f0d776b0-fd96-4282-aecd-722d89b96cff","ownedRelationship":[{"id":"89988082-a6b7-408a-99e5-9601ce0a1bf6","eClass":"sysml:ParameterMembership","data":{"elementId":"6ec39eba-a272-4db0-8c70-d16ea7517f86","ownedRelatedElement":[{"id":"51df5f6b-5cb6-48e7-9677-080c823eacca","eClass":"sysml:Feature","data":{"elementId":"53f44fae-071d-4263-9fde-7dc008d14df0","ownedRelationship":[{"id":"e741aa76-c4bc-4afc-95a6-d9d947a279c6","eClass":"sysml:FeatureValue","data":{"elementId":"a938aad2-2fb1-4258-b1c0-730ab660880b","ownedRelatedElement":[{"id":"ef20405c-9d4d-4ee5-bd10-b353bd9d8e1f","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"0e23e1a9-17f0-4ee1-bd4e-9ca406eece66","ownedRelationship":[{"id":"8938cb64-37ff-421a-82c4-3e1fbc43ddd4","eClass":"sysml:Membership","data":{"elementId":"4b50d9b9-0289-4edc-852e-b9037b2ef60f","memberElement":"33466a30-d081-4d0d-a3dc-142c59b82000"}}]}}]}}],"direction":"in"}}]}},{"id":"3045651a-778a-43ae-bdb0-b7072ec4bac1","eClass":"sysml:Membership","data":{"elementId":"1a41f919-9831-4a8b-b7ba-5c628414b769","memberElement":"6b9a0d65-eff4-4d98-8b25-d6e24d0486e9"}}]}}]}}],"direction":"in"}}]}},{"id":"fad2ff7a-212e-42e4-9a73-3f8c603d3d37","eClass":"sysml:ParameterMembership","data":{"elementId":"57db7214-0217-449d-8a9b-af78d0334a26","ownedRelatedElement":[{"id":"7c261030-6e18-4072-b4d8-5e700f06f982","eClass":"sysml:Feature","data":{"elementId":"a885a849-350f-41a8-acae-f10b40a21ae8","ownedRelationship":[{"id":"4fc87293-d300-4bdf-b824-c54f9fc9dd91","eClass":"sysml:FeatureValue","data":{"elementId":"1b1e8333-e3e8-4ebc-89b5-1a30a9f54b19","ownedRelatedElement":[{"id":"8d96afc1-c7dd-42ad-a350-57f0b800cc38","eClass":"sysml:LiteralRational","data":{"elementId":"576e66e8-67b6-49c2-957d-738dc65e0309"}}]}}],"direction":"in"}}]}}],"operator":"!="}}]}}],"direction":"in"}}]}},{"id":"03c4fde9-59c7-4499-b22c-f76128218acc","eClass":"sysml:ParameterMembership","data":{"elementId":"8b82cb63-e3d2-410a-86d5-053d9c4de34e","ownedRelatedElement":[{"id":"7949920d-fef9-4ada-a1fd-6146f740a9af","eClass":"sysml:Feature","data":{"elementId":"13999012-8669-458a-9a12-09a4344c25c5","ownedRelationship":[{"id":"7f0c0cbc-d241-42e4-9631-63195ec2387e","eClass":"sysml:FeatureValue","data":{"elementId":"4ae968bd-9835-490f-b122-70579a109558","ownedRelatedElement":[{"id":"4ca5a50e-d116-4b29-9e5c-bff45c6685d9","eClass":"sysml:OperatorExpression","data":{"elementId":"097d302b-bd39-4fdf-b37d-bfaf274d684a","ownedRelationship":[{"id":"c1b9246d-6c33-4efa-8349-63eca55ed258","eClass":"sysml:ParameterMembership","data":{"elementId":"acbda919-a449-4e50-8769-21c05c333f6e","ownedRelatedElement":[{"id":"7b99f3f9-9a46-4dc1-b31b-15b4f243e8e2","eClass":"sysml:Feature","data":{"elementId":"72c64d09-668a-4881-94f2-a02ee97e7f08","ownedRelationship":[{"id":"1ccd4617-8a24-40c1-b1bf-e14c57948d68","eClass":"sysml:FeatureValue","data":{"elementId":"df287639-e735-491a-8fe5-77e377244ea0","ownedRelatedElement":[{"id":"3b7a7f00-42b4-4962-8249-201ea3546292","eClass":"sysml:FeatureChainExpression","data":{"elementId":"39c623a8-db65-483c-b49d-6476f1eb02a7","ownedRelationship":[{"id":"c4b1bdfe-008a-42e2-a0da-560ef61c0657","eClass":"sysml:ParameterMembership","data":{"elementId":"dd1c5acd-02d0-40d5-a509-f934c915227d","ownedRelatedElement":[{"id":"f952d5b2-527e-4487-ad14-a501cbb4ec32","eClass":"sysml:Feature","data":{"elementId":"2302b015-7719-4042-9929-e4ad7cf7b1eb","ownedRelationship":[{"id":"ce4564b8-6fe7-49fc-a146-5d14b3c7405e","eClass":"sysml:FeatureValue","data":{"elementId":"6b6dae15-eda9-4734-8313-71897cfb4fe1","ownedRelatedElement":[{"id":"cbb045fe-e306-4bf8-84ed-e105822603f7","eClass":"sysml:FeatureReferenceExpression","data":{"elementId":"dac96cd8-1b64-4b8f-86d2-f9c162cb33a2","ownedRelationship":[{"id":"1fcde9a2-93af-4f46-b5af-0e6b2c94f94e","eClass":"sysml:Membership","data":{"elementId":"ec45938e-93b4-4039-8f0c-8fb5fae4bea0","memberElement":"33466a30-d081-4d0d-a3dc-142c59b82000"}}]}}]}}],"direction":"in"}}]}},{"id":"c3ae80a8-eeb0-4fd4-8d1a-36b18bf83520","eClass":"sysml:Membership","data":{"elementId":"b81cd454-88de-46fa-b993-db22a7a5d0bb","memberElement":"4d2f8465-d748-4b68-a5a8-d61c03031bd3"}}]}}]}}],"direction":"in"}}]}},{"id":"11ebb0eb-72cc-46a0-98f6-c135660b2e82","eClass":"sysml:ParameterMembership","data":{"elementId":"db89e0f5-6177-4b68-8850-44f7e32dd602","ownedRelatedElement":[{"id":"479d4b8c-c79b-42d3-805a-afc1dfa21852","eClass":"sysml:Feature","data":{"elementId":"83978f47-55dd-4399-a8b4-e84a7c03c13d","ownedRelationship":[{"id":"20aa93bd-a3fc-4985-b34a-c7d0cb4f8a98","eClass":"sysml:FeatureValue","data":{"elementId":"5ab031d1-3718-46a1-ae8c-c1eb2f226c3d","ownedRelatedElement":[{"id":"0f874cca-dc06-4430-89b8-a00dee51eaee","eClass":"sysml:LiteralInteger","data":{"elementId":"88c30f81-c3ce-4804-9142-c0b33f51dcd3"}}]}}],"direction":"in"}}]}}],"operator":"=="}}]}}],"direction":"in"}}]}}],"operator":"|"}}]}}],"direction":"in"}}]}}],"operator":"&"}}]}}],"isComposite":true}}],"kind":"requirement"}}]}}]}}]}}]}}]}}]}', '2026-05-20 16:21:07.743843+00', '2026-05-20 16:21:07.743843+00', false); -- diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/ASTTransformer.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/ASTTransformer.java index 38c897125..d40f5d17d 100644 --- a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/ASTTransformer.java +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/ASTTransformer.java @@ -19,6 +19,8 @@ import java.util.List; import java.util.ListIterator; import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; @@ -28,6 +30,7 @@ import org.eclipse.sirius.components.representations.Message; import org.eclipse.syson.services.DeleteService; import org.eclipse.syson.sysml.helper.EMFUtils; +import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; import org.eclipse.syson.sysml.parser.AstTreeParser; import org.eclipse.syson.sysml.parser.ContainmentReferenceHandler; import org.eclipse.syson.sysml.parser.EAttributeHandler; @@ -57,6 +60,8 @@ public class ASTTransformer { private final NonContainmentReferenceHandler nonContainmentReferenceHandler; + private final MetamodelQueryElementService metamodelQueryElementService; + public ASTTransformer() { this.messageReporter = new MessageReporter(); this.nonContainmentReferenceHandler = new NonContainmentReferenceHandler(this.messageReporter); @@ -64,6 +69,7 @@ public ASTTransformer() { var proxyResolver = new ProxyResolver(this.messageReporter); var astObjectParser = new EAttributeHandler(this.messageReporter); this.astTreeParser = new AstTreeParser(astContainmentReferenceParser, this.nonContainmentReferenceHandler, proxyResolver, astObjectParser, this.messageReporter); + this.metamodelQueryElementService = new MetamodelQueryElementService(); } public Resource convertResource(final InputStream input, final ResourceSet resourceSet) { @@ -89,43 +95,59 @@ public Resource convertResource(final InputStream input, final ResourceSet resou * * @param input * the textual representation - * @param resourceSet - * the current {@link ResourceSet} * @param parentElement - * the parent element in which created element will be added. + * the parent element in which created element will be added. May be null if we only validate. + * @return the list of the created elements + */ + public List convertToElements(InputStream input, Element parentElement) { + return this.convertToElements(input, parentElement, null, (messages) -> Boolean.FALSE); + } + + /** + * Convert the given SysML text into Elements and add them into the given parent. + * + * @param input + * the textual representation + * @param parentElement + * the parent element in which created element will be added. May be null if we only validate. + * @param contentSelector + * a function to select the SysML Elements of actual interest from the result of syside's parsing. + * @param shouldRevertPredicate + * predicate invoked after the operation is done to determine, given the impact (as reported by the + * messages), if it should be reverted. * @return the list of the created elements */ - public List convertToElements(final InputStream input, final ResourceSet resourceSet, Element parentElement) { + public List convertToElements(InputStream input, Element parentElement, Function, List> contentSelector, Predicate> shouldRevertPredicate) { List result = List.of(); if (input != null) { final JsonNode astJson = this.readAst(input); if (astJson != null) { this.logger.info("Create the Root eObject containment structure"); - result = this.extractContent(this.astTreeParser.parseAst(astJson)); + List parsedAst = this.astTreeParser.parseAst(astJson); + var contents = this.getNamespaceContent(parsedAst); + if (contentSelector != null) { + result = contentSelector.apply(contents); + } else { + result = contents; + } this.logger.info("File Parsed"); + List undo = new ArrayList<>(); for (Element root : result) { - this.addInParent(parentElement, root); + undo.add(this.addInParent(parentElement, root)); } this.logger.info("Elements added in parent"); this.fixAndResolve(result); + + if (shouldRevertPredicate.test(this.messageReporter.getReportedMessages())) { + undo.reversed().forEach(Runnable::run); + } } } return result; } - private void fixAndResolve(List result) { - this.preResolvingFixingPhase(result); - - List proxiedReferences = this.nonContainmentReferenceHandler.getProxiesToResolve(); - this.logger.info("{} references to resolve.", proxiedReferences.size()); - this.astTreeParser.resolveAllReference(proxiedReferences); - this.logger.info("End of references resolving"); - - this.postResolvingFixingPhase(result); - } - - private List extractContent(List roots) { + private List getNamespaceContent(List roots) { return roots.stream().filter(Namespace.class::isInstance) .map(Namespace.class::cast) .flatMap(ns -> ns.getOwnedRelationship().stream()) @@ -143,20 +165,57 @@ private List getChildren(Relationship relationship) { return children; } - private void addInParent(Element parent, Element child) { + private void fixAndResolve(List result) { + this.preResolvingFixingPhase(result); + + List proxiedReferences = this.nonContainmentReferenceHandler.getProxiesToResolve(); + this.logger.info("{} references to resolve.", proxiedReferences.size()); + this.astTreeParser.resolveAllReference(proxiedReferences); + this.logger.info("End of references resolving"); + + this.postResolvingFixingPhase(result); + } + + /** + * Add a child element inside a parent, using the appropriate relationship. + * + * @param parent + * the parent element. + * @param child + * the child to add. + * @return a Runnable that can be executed to undo the addition. + */ + private Runnable addInParent(Element parent, Element child) { + Runnable undo = () -> { + // No-op + }; if (child instanceof Import imp) { parent.getOwnedRelationship().add(imp); + undo = () -> parent.getOwnedRelationship().remove(imp); + } else if (child instanceof Expression expr) { + var compatibleOwnerships = this.metamodelQueryElementService.getCompatibleExpressionOwnerships(parent); + if (!compatibleOwnerships.isEmpty()) { + var selectedOwnership = compatibleOwnerships.get(0); + selectedOwnership.getOwnedRelatedElement().add(expr); + parent.getOwnedRelationship().add(selectedOwnership); + selectedOwnership.getOwnedRelatedElement().add(child); + undo = () -> parent.getOwnedRelationship().remove(selectedOwnership); + } } else if (child instanceof Feature && parent instanceof Type) { Membership membership = SysmlFactory.eINSTANCE.createFeatureMembership(); - parent.getOwnedRelationship().add(membership); membership.getOwnedRelatedElement().add(child); + parent.getOwnedRelationship().add(membership); + undo = () -> parent.getOwnedRelationship().remove(membership); } else if (parent instanceof Package || SysmlPackage.eINSTANCE.getNamespace().equals(parent.eClass())) { Membership membership = SysmlFactory.eINSTANCE.createOwningMembership(); membership.getOwnedRelatedElement().add(child); parent.getOwnedRelationship().add(membership); + undo = () -> parent.getOwnedRelationship().remove(membership); } else if (child instanceof Relationship rel) { parent.getOwnedRelationship().add(rel); + undo = () -> parent.getOwnedRelationship().remove(rel); } + return undo; } private void postResolvingFixingPhase(List rootSysmlObjects) { @@ -169,11 +228,11 @@ private void postResolvingFixingPhase(List rootSysmlObjects) } /** - * The current implementation of the parser does not force the memberFeature of EndFeatureMembership to have "isEnd = true" like stated in the SysML specification see "8.3.3.3.3 - * EndFeatureMembership". + * The current implementation of the parser does not force the memberFeature of EndFeatureMembership to have "isEnd + * = true" like stated in the SysML specification see "8.3.3.3.3 EndFeatureMembership". * * @param root - * the root of the imported object + * the root of the imported object */ private void fixEndFeatureMembership(EObject root) { EMFUtils.allContainedObjectOfType(root, EndFeatureMembership.class) diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/datafetchers/MutationCreateExpressionDataFetcher.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/datafetchers/MutationCreateExpressionDataFetcher.java new file mode 100644 index 000000000..b2f6e9a00 --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/datafetchers/MutationCreateExpressionDataFetcher.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.datafetchers; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.sirius.components.annotations.spring.graphql.MutationDataFetcher; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.IEditingContextDispatcher; +import org.eclipse.sirius.components.graphql.api.IExceptionWrapper; +import org.eclipse.syson.sysml.dto.CreateExpressionInput; + +import graphql.schema.DataFetchingEnvironment; +import tools.jackson.databind.ObjectMapper; + +/** + * Data fetcher for the {@code EditingContext#createExpression} mutation. + * + * @author pcdavid + */ +@MutationDataFetcher(type = "Mutation", field = "createExpression") +public class MutationCreateExpressionDataFetcher implements IDataFetcherWithFieldCoordinates> { + private static final String INPUT_ARGUMENT = "input"; + + private final ObjectMapper objectMapper; + + private final IExceptionWrapper exceptionWrapper; + + private final IEditingContextDispatcher editingContextDispatcher; + + public MutationCreateExpressionDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEditingContextDispatcher editingContextDispatcher) { + this.objectMapper = Objects.requireNonNull(objectMapper); + this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper); + this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher); + } + + @Override + public CompletableFuture get(DataFetchingEnvironment environment) throws Exception { + Object argument = environment.getArgument(INPUT_ARGUMENT); + var input = this.objectMapper.convertValue(argument, CreateExpressionInput.class); + + return this.exceptionWrapper.wrapMono(() -> this.editingContextDispatcher.dispatchMutation(input.editingContextId(), input), input).toFuture(); + } +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/datafetchers/MutationEditExpressionDataFetcher.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/datafetchers/MutationEditExpressionDataFetcher.java new file mode 100644 index 000000000..942b8441e --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/datafetchers/MutationEditExpressionDataFetcher.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.datafetchers; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.sirius.components.annotations.spring.graphql.MutationDataFetcher; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates; +import org.eclipse.sirius.components.graphql.api.IEditingContextDispatcher; +import org.eclipse.sirius.components.graphql.api.IExceptionWrapper; +import org.eclipse.syson.sysml.dto.EditExpressionInput; + +import graphql.schema.DataFetchingEnvironment; +import tools.jackson.databind.ObjectMapper; + +/** + * Data fetcher for the {@code EditingContext#editExpression} mutation. + * + * @author pcdavid + */ +@MutationDataFetcher(type = "Mutation", field = "editExpression") +public class MutationEditExpressionDataFetcher implements IDataFetcherWithFieldCoordinates> { + private static final String INPUT_ARGUMENT = "input"; + + private final ObjectMapper objectMapper; + + private final IExceptionWrapper exceptionWrapper; + + private final IEditingContextDispatcher editingContextDispatcher; + + public MutationEditExpressionDataFetcher(ObjectMapper objectMapper, IExceptionWrapper exceptionWrapper, IEditingContextDispatcher editingContextDispatcher) { + this.objectMapper = Objects.requireNonNull(objectMapper); + this.exceptionWrapper = Objects.requireNonNull(exceptionWrapper); + this.editingContextDispatcher = Objects.requireNonNull(editingContextDispatcher); + } + + @Override + public CompletableFuture get(DataFetchingEnvironment environment) throws Exception { + Object argument = environment.getArgument(INPUT_ARGUMENT); + var input = this.objectMapper.convertValue(argument, EditExpressionInput.class); + + return this.exceptionWrapper.wrapMono(() -> this.editingContextDispatcher.dispatchMutation(input.editingContextId(), input), input).toFuture(); + } +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/CreateExpressionInput.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/CreateExpressionInput.java new file mode 100644 index 000000000..7cdddbc3c --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/CreateExpressionInput.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.dto; + +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IInput; + +/** + * The input object of the createExpression operation. + * + * @author pcdavid + */ +public record CreateExpressionInput(UUID id, String editingContextId, String parentElementId, String expressionText) implements IInput { +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/CreateExpressionSuccessPayload.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/CreateExpressionSuccessPayload.java new file mode 100644 index 000000000..e319cde43 --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/CreateExpressionSuccessPayload.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.dto; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.representations.Message; + +/** + * The result returned by the {@code createExpression} mutation on success. + * + * @author pcdavid + */ +public record CreateExpressionSuccessPayload(UUID id, String newExpressionId, List messages) implements IPayload { + public CreateExpressionSuccessPayload { + Objects.requireNonNull(id); + Objects.requireNonNull(newExpressionId); + Objects.requireNonNull(messages); + } +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/EditExpressionInput.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/EditExpressionInput.java new file mode 100644 index 000000000..dac6921c0 --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/EditExpressionInput.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.dto; + +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IInput; + +/** + * The input object of the {@code editExpression} mutation. + * + * @author pcdavid + */ +public record EditExpressionInput(UUID id, String editingContextId, String expressionElementId, String newExpressionText) implements IInput { +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/EditExpressionSuccessPayload.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/EditExpressionSuccessPayload.java new file mode 100644 index 000000000..068763069 --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/EditExpressionSuccessPayload.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.dto; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.representations.Message; + +/** + * The result returned by the {@code editExpression} mutation on success. + * + * @author pcdavid + */ +public record EditExpressionSuccessPayload(UUID id, String newExpressionId, List messages) implements IPayload { + public EditExpressionSuccessPayload { + Objects.requireNonNull(id); + Objects.requireNonNull(newExpressionId); + Objects.requireNonNull(messages); + } +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/CreateExpressionEventHandler.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/CreateExpressionEventHandler.java new file mode 100644 index 000000000..15ce52584 --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/CreateExpressionEventHandler.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.services; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.api.ChangeDescription; +import org.eclipse.sirius.components.collaborative.api.ChangeKind; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventHandler; +import org.eclipse.sirius.components.collaborative.api.Monitoring; +import org.eclipse.sirius.components.collaborative.messages.ICollaborativeMessageService; +import org.eclipse.sirius.components.core.api.ErrorPayload; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IIdentityService; +import org.eclipse.sirius.components.core.api.IInput; +import org.eclipse.sirius.components.core.api.IObjectSearchService; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.dto.CreateExpressionInput; +import org.eclipse.syson.sysml.dto.CreateExpressionSuccessPayload; +import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; +import org.eclipse.syson.sysml.services.api.ISysMLExpressionEditor; +import org.springframework.stereotype.Service; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import reactor.core.publisher.Sinks.Many; +import reactor.core.publisher.Sinks.One; + +/** + * Handler used to execute the {@code createExpression} operation. + * + * @author pcdavid + */ +@Service +public class CreateExpressionEventHandler implements IEditingContextEventHandler { + + private final IObjectSearchService objectSearchService; + + private final IIdentityService identityService; + + private final ISysMLExpressionEditor expressionEditor; + + private final MetamodelQueryElementService metamodelQueryElementService; + + private final ICollaborativeMessageService messageService; + + private final Counter counter; + + public CreateExpressionEventHandler(IObjectSearchService objectSearchService, IIdentityService identityService, ISysMLExpressionEditor expressionEditor, + ICollaborativeMessageService messageService, MeterRegistry meterRegistry) { + this.objectSearchService = Objects.requireNonNull(objectSearchService); + this.identityService = Objects.requireNonNull(identityService); + this.expressionEditor = Objects.requireNonNull(expressionEditor); + this.metamodelQueryElementService = new MetamodelQueryElementService(); + this.messageService = Objects.requireNonNull(messageService); + this.counter = Counter.builder(Monitoring.EVENT_HANDLER) + .tag(Monitoring.NAME, this.getClass().getSimpleName()) + .register(meterRegistry); + } + + @Override + public boolean canHandle(IEditingContext editingContext, IInput input) { + return editingContext instanceof IEMFEditingContext && input instanceof CreateExpressionInput; + } + + @Override + public void handle(One payloadSink, Many changeDescriptionSink, IEditingContext editingContext, IInput input) { + this.counter.increment(); + IPayload payload; + ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, editingContext.getId(), input); + + if (input instanceof CreateExpressionInput createExpressionInput && editingContext instanceof IEMFEditingContext emfEditingContext) { + Optional optionalParentElement = this.objectSearchService.getObject(editingContext, createExpressionInput.parentElementId()) + .filter(Element.class::isInstance) + .map(Element.class::cast); + if (optionalParentElement.isPresent()) { + if (this.metamodelQueryElementService.hasSingleExpressionDefinition(optionalParentElement.get())) { + payload = new ErrorPayload(input.id(), "The parent element already has an expression"); + } else { + var result = this.expressionEditor.createExpression(emfEditingContext, optionalParentElement.get(), createExpressionInput.expressionText()); + if (result.createdExpression() != null) { + var createdExpression = result.createdExpression(); + var createdExpressionId = this.identityService.getId(createdExpression); + changeDescription = new ChangeDescription(ChangeKind.SEMANTIC_CHANGE, editingContext.getId(), input); + payload = new CreateExpressionSuccessPayload(input.id(), createdExpressionId, result.messages()); + } else { + payload = new ErrorPayload(input.id(), result.messages()); + } + } + } else { + payload = new ErrorPayload(input.id(), this.messageService.notFound()); + } + } else { + payload = new ErrorPayload(input.id(), this.messageService.invalidInput(CreateExpressionInput.class.getName(), input.getClass().getName())); + } + + payloadSink.tryEmitValue(payload); + changeDescriptionSink.tryEmitNext(changeDescription); + } +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/EditExpressionEventHandler.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/EditExpressionEventHandler.java new file mode 100644 index 000000000..02d6077ab --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/EditExpressionEventHandler.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.services; + +import java.util.Objects; +import java.util.Optional; + +import org.eclipse.sirius.components.collaborative.api.ChangeDescription; +import org.eclipse.sirius.components.collaborative.api.ChangeKind; +import org.eclipse.sirius.components.collaborative.api.IEditingContextEventHandler; +import org.eclipse.sirius.components.collaborative.api.Monitoring; +import org.eclipse.sirius.components.collaborative.messages.ICollaborativeMessageService; +import org.eclipse.sirius.components.core.api.ErrorPayload; +import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IIdentityService; +import org.eclipse.sirius.components.core.api.IInput; +import org.eclipse.sirius.components.core.api.IObjectSearchService; +import org.eclipse.sirius.components.core.api.IPayload; +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Expression; +import org.eclipse.syson.sysml.dto.EditExpressionInput; +import org.eclipse.syson.sysml.dto.EditExpressionSuccessPayload; +import org.eclipse.syson.sysml.services.api.ISysMLExpressionEditor; +import org.springframework.stereotype.Service; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import reactor.core.publisher.Sinks; + +/** + * Event handler for the {@code editExpression} mutation. + * + * @author pcdavid + */ +@Service +public class EditExpressionEventHandler implements IEditingContextEventHandler { + + private final IObjectSearchService objectSearchService; + + private final IIdentityService identityService; + + private final ICollaborativeMessageService messageService; + + private final ISysMLExpressionEditor expressionEditor; + + private final Counter counter; + + public EditExpressionEventHandler(IObjectSearchService objectSearchService, IIdentityService identityService, ICollaborativeMessageService messageService, ISysMLExpressionEditor expressionEditor, + MeterRegistry meterRegistry) { + this.objectSearchService = Objects.requireNonNull(objectSearchService); + this.identityService = Objects.requireNonNull(identityService); + this.messageService = Objects.requireNonNull(messageService); + this.expressionEditor = Objects.requireNonNull(expressionEditor); + this.counter = Counter.builder(Monitoring.EVENT_HANDLER) + .tag(Monitoring.NAME, this.getClass().getSimpleName()) + .register(meterRegistry); + } + + @Override + public boolean canHandle(IEditingContext editingContext, IInput input) { + return editingContext instanceof IEMFEditingContext && input instanceof EditExpressionInput; + } + + @Override + public void handle(Sinks.One payloadSink, Sinks.Many changeDescriptionSink, IEditingContext editingContext, IInput input) { + this.counter.increment(); + IPayload payload; + ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, editingContext.getId(), input); + + if (input instanceof EditExpressionInput editExpressionInput && editingContext instanceof IEMFEditingContext emfEditingContext) { + Optional optionalExpression = this.objectSearchService.getObject(editingContext, editExpressionInput.expressionElementId()) + .filter(Expression.class::isInstance) + .map(Expression.class::cast); + Optional optionalParent = Optional.empty(); + if (optionalExpression.isPresent()) { + optionalParent = Optional.ofNullable(optionalExpression.get().getOwner()); + } + + if (optionalParent.isPresent() && optionalExpression.isPresent()) { + var result = this.expressionEditor.editExpression(emfEditingContext, optionalParent.get(), optionalExpression.get(), editExpressionInput.newExpressionText()); + if (result.createdExpression() != null) { + var newExpression = result.createdExpression(); + var newExpressionId = this.identityService.getId(newExpression); + + changeDescription = new ChangeDescription(ChangeKind.SEMANTIC_CHANGE, editingContext.getId(), input); + payload = new EditExpressionSuccessPayload(input.id(), newExpressionId, result.messages()); + } else { + payload = new ErrorPayload(input.id(), result.messages()); + } + } else { + payload = new ErrorPayload(input.id(), this.messageService.notFound()); + } + } else { + payload = new ErrorPayload(input.id(), this.messageService.invalidInput(EditExpressionInput.class.getName(), input.getClass().getName())); + } + + payloadSink.tryEmitValue(payload); + changeDescriptionSink.tryEmitNext(changeDescription); + } +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/InsertTextualSysMLv2EventHandler.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/InsertTextualSysMLv2EventHandler.java similarity index 60% rename from backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/InsertTextualSysMLv2EventHandler.java rename to backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/InsertTextualSysMLv2EventHandler.java index 30b2ad919..e6d02fdcd 100644 --- a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/dto/InsertTextualSysMLv2EventHandler.java +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/InsertTextualSysMLv2EventHandler.java @@ -10,12 +10,12 @@ * Contributors: * Obeo - initial API and implementation *******************************************************************************/ -package org.eclipse.syson.sysml.dto; +package org.eclipse.syson.sysml.services; -import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import org.eclipse.sirius.components.collaborative.api.ChangeDescription; import org.eclipse.sirius.components.collaborative.api.ChangeKind; @@ -31,10 +31,9 @@ import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; import org.eclipse.sirius.components.representations.Message; import org.eclipse.sirius.components.representations.MessageLevel; -import org.eclipse.syson.sysml.ASTTransformer; import org.eclipse.syson.sysml.Element; -import org.eclipse.syson.sysml.SysmlToAst; -import org.eclipse.syson.sysml.textual.utils.Status; +import org.eclipse.syson.sysml.dto.InsertTextualSysMLv2Input; +import org.eclipse.syson.sysml.services.api.ISysMLTextImporter; import org.springframework.stereotype.Service; import io.micrometer.core.instrument.Counter; @@ -52,17 +51,16 @@ public class InsertTextualSysMLv2EventHandler implements IEditingContextEventHan private final IObjectSearchService objectSearchService; - private final ICollaborativeMessageService messageService; + private final ISysMLTextImporter sysmlTextImporter; - private final SysmlToAst sysmlToAst; + private final ICollaborativeMessageService messageService; private final Counter counter; - public InsertTextualSysMLv2EventHandler(IObjectSearchService objectSearchService, ICollaborativeMessageService messageService, - SysmlToAst sysmlToAst, MeterRegistry meterRegistry) { + public InsertTextualSysMLv2EventHandler(IObjectSearchService objectSearchService, ISysMLTextImporter sysmlTextImporter, ICollaborativeMessageService messageService, MeterRegistry meterRegistry) { this.objectSearchService = Objects.requireNonNull(objectSearchService); + this.sysmlTextImporter = Objects.requireNonNull(sysmlTextImporter); this.messageService = Objects.requireNonNull(messageService); - this.sysmlToAst = Objects.requireNonNull(sysmlToAst); this.counter = Counter.builder(Monitoring.EVENT_HANDLER) .tag(Monitoring.NAME, this.getClass().getSimpleName()) .register(meterRegistry); @@ -85,12 +83,12 @@ public void handle(One payloadSink, Many changeDesc if (input instanceof InsertTextualSysMLv2Input insertTextualInput && editingContext instanceof IEMFEditingContext emfEditingContext) { messages = new ArrayList<>(); - var parentObjectId = insertTextualInput.objectId(); - var parentElement = this.getParentElement(parentObjectId, emfEditingContext); - if (parentElement != null) { - var transformer = new ASTTransformer(); - var newObjects = this.convert(insertTextualInput, emfEditingContext, transformer, parentElement, messages); - messages.addAll(transformer.getTransformationMessages()); + var optionalParentElement = this.resolveElement(emfEditingContext, insertTextualInput.objectId()); + if (optionalParentElement.isPresent()) { + var parent = optionalParentElement.get(); + var textualContent = insertTextualInput.textualContent(); + + var newObjects = this.sysmlTextImporter.importSysMLText(emfEditingContext, parent, textualContent, messages); if (!newObjects.isEmpty()) { payload = new SuccessPayload(input.id(), messages); changeDescription = new ChangeDescription(ChangeKind.SEMANTIC_CHANGE, editingContext.getId(), input); @@ -108,44 +106,10 @@ public void handle(One payloadSink, Many changeDesc changeDescriptionSink.tryEmitNext(changeDescription); } - private Element getParentElement(String parentObjectId, IEMFEditingContext emfEditingContext) { - var parentObject = this.objectSearchService.getObject(emfEditingContext, parentObjectId); - if (parentObject.isPresent()) { - var object = parentObject.get(); - if (object instanceof Element parentElement) { - return parentElement; - } - } - return null; - } - - private List convert(InsertTextualSysMLv2Input insertTextualInput, IEMFEditingContext emfEditingContext, ASTTransformer transformer, Element parentElement, List messages) { - var textualContent = insertTextualInput.textualContent(); - var resourceSet = emfEditingContext.getDomain().getResourceSet(); - var inputStream = new ByteArrayInputStream(textualContent.getBytes()); - var astParsingResult = this.sysmlToAst.convert(inputStream, ".sysml"); - messages.addAll(astParsingResult.reports().stream() - .map(this::toMessage) - .filter(Objects::nonNull) - .toList()); - if (astParsingResult.ast().isPresent()) { - return transformer.convertToElements(astParsingResult.ast().get(), resourceSet, parentElement); - } else { - return List.of(); - } + private Optional resolveElement(IEMFEditingContext emfEditingContext, String objectId) { + return this.objectSearchService.getObject(emfEditingContext, objectId) + .filter(Element.class::isInstance) + .map(Element.class::cast); } - private Message toMessage(Status status) { - MessageLevel msgLevel = switch (status.severity()) { - case INFO -> MessageLevel.INFO; - case WARNING -> MessageLevel.WARNING; - case ERROR -> MessageLevel.ERROR; - default -> null; - }; - if (msgLevel != null) { - return new Message(status.message(), msgLevel); - } else { - return null; - } - } } diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/SysideExpressionEditor.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/SysideExpressionEditor.java new file mode 100644 index 000000000..1989550b2 --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/SysideExpressionEditor.java @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.services; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.sirius.components.representations.Message; +import org.eclipse.sirius.components.representations.MessageLevel; +import org.eclipse.syson.sysml.ASTTransformer; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Expression; +import org.eclipse.syson.sysml.Feature; +import org.eclipse.syson.sysml.Function; +import org.eclipse.syson.sysml.Relationship; +import org.eclipse.syson.sysml.StateUsage; +import org.eclipse.syson.sysml.SysmlToAst; +import org.eclipse.syson.sysml.TransitionFeatureKind; +import org.eclipse.syson.sysml.TransitionFeatureMembership; +import org.eclipse.syson.sysml.TransitionUsage; +import org.eclipse.syson.sysml.services.api.ExpressionCreationResult; +import org.eclipse.syson.sysml.services.api.ISysMLExpressionEditor; +import org.eclipse.syson.sysml.textual.utils.Status; +import org.springframework.stereotype.Service; + +/** + * syside-based implementation of {@link ISysMLExpressionEditor}. + * + * @author pcdavid + */ +@Service +public class SysideExpressionEditor implements ISysMLExpressionEditor { + + private final SysmlToAst sysmlToAst; + + public SysideExpressionEditor(SysmlToAst sysmlToAst) { + this.sysmlToAst = Objects.requireNonNull(sysmlToAst); + } + + @Override + public ExpressionCreationResult createExpression(IEMFEditingContext emfEditingContext, Element parentElement, String expressionText) { + return this.createOrEditExpression(emfEditingContext, parentElement, Optional.empty(), expressionText); + } + + @Override + public ExpressionCreationResult editExpression(IEMFEditingContext emfEditingContext, Element parentElement, Expression expression, String expressionText) { + return this.createOrEditExpression(emfEditingContext, parentElement, Optional.of(expression), expressionText); + } + + private ExpressionCreationResult createOrEditExpression(IEMFEditingContext emfEditingContext, Element parentElement, Optional expressionToReplace, String expressionText) { + List newObjects = List.of(); + List messages = new ArrayList<>(); + + // Needed to force the syside parser to treat the input text as an Expression. + var wrappedText = this.wrapPlainExpression(parentElement, expressionText); + + // Phase 1: parse the raw text into a JSON AST using syside-cli + var inputStream = new ByteArrayInputStream(wrappedText.getBytes()); + var astParsingResult = this.sysmlToAst.convert(inputStream, ".sysml"); + messages.addAll(this.toMessages(astParsingResult.reports())); + + // Phase 2: "parse" the resulting JSON AST into actual SySMLv2 Elements and integrate them into + // parentElement + if (astParsingResult.ast().isPresent()) { + var transformer = new ASTTransformer(); + newObjects = transformer.convertToElements(astParsingResult.ast().get(), parentElement, this::unwrap, this::containsErrors); + messages.addAll(transformer.getTransformationMessages()); + } + boolean success = !this.containsErrors(messages); + var optionalResult = this.getNewExpression(newObjects); + + if (success && optionalResult.isPresent()) { + if (expressionToReplace.isPresent()) { + this.replace(expressionToReplace.get(), optionalResult.get()); + } + return new ExpressionCreationResult(optionalResult.get(), messages); + } else { + return new ExpressionCreationResult(null, messages); + } + } + + private boolean containsErrors(List messages) { + return messages.stream().anyMatch(message -> message.level().equals(MessageLevel.WARNING) || message.level().equals(MessageLevel.ERROR)); + } + + private Optional getNewExpression(List newObjects) { + if (!newObjects.isEmpty() && newObjects.get(0) instanceof Relationship relationship && !relationship.getOwnedRelatedElement().isEmpty() + && relationship.getOwnedRelatedElement().get(0) instanceof Expression newExpression) { + return Optional.of(newExpression); + } else { + return Optional.empty(); + } + } + + private void replace(Expression expression, Expression newExpression) { + if (newExpression.getOwningRelationship() != null) { + EcoreUtil.remove(newExpression.getOwningRelationship()); + } + if (expression.getOwningRelationship() != null) { + EList relatedElements = expression.getOwningRelationship().getOwnedRelatedElement(); + int index = relatedElements.indexOf(expression); + relatedElements.add(index, newExpression); + relatedElements.remove(expression); + } + } + + /** + * Wraps the plain expression entered by the user into a top-level construct of the appropriate kind to force the + * syside parser to actually interpret it as an Expression. + * + * @param parentElement + * @param plainExpression + * @return + */ + private String wrapPlainExpression(Element parentElement, String plainExpression) { + UUID uniqueName = UUID.randomUUID(); + String template = ""; + if (parentElement instanceof TransitionUsage) { + // Will produce a more complex structure inside which we'll find the actual + // TransitionFeatureMembership[kind=GUARD] -> + // Expression. See extractGuardExpression(). + template = """ + state <'%s'> { + entry action init; + state off; + transition first init if %s then off; + } + """; + } else if (parentElement instanceof Function || parentElement instanceof Expression) { + // Will produce ConstraintDefition -> ResultExpressionMembership -> Expression + template = "constraint def <'%s'> { %s }"; + } else if (parentElement instanceof Feature) { + // Will produce AttributeUsage -> FeatureValue -> Expression + template = "attribute <'%s'> = %s;"; + } + return String.format(template, uniqueName, plainExpression); + } + + /** + * Extracts the expression and its parent relationship once they have been parsed and linked. This is the reverse of + * {@link #wrapPlainExpression(Element, String)}. + * + * @param roots + * @return + */ + private List unwrap(List roots) { + Element result = null; + var parentElement = roots.stream().findFirst().orElse(null); + if (parentElement instanceof StateUsage wrappingState) { + result = this.extractGuardExpression(wrappingState); + } else if (parentElement instanceof Function || parentElement instanceof Expression) { + result = parentElement.getOwnedRelationship().stream().map(Element.class::cast).findFirst().orElse(null); + } else if (parentElement instanceof Feature) { + result = parentElement.getOwnedRelationship().stream().map(Element.class::cast).findFirst().orElse(null); + } + + return Optional.ofNullable(result).stream().toList(); + } + + private Element extractGuardExpression(StateUsage wrappingState) { + Element result; + result = wrappingState.getOwnedRelationship().get(2) + .getOwnedRelatedElement().get(0) + .getOwnedRelationship().stream() + .filter(TransitionFeatureMembership.class::isInstance) + .map(TransitionFeatureMembership.class::cast) + .filter(tfm -> tfm.getKind() == TransitionFeatureKind.GUARD) + .findFirst() + .orElse(null); + return result; + } + + private List toMessages(List reports) { + return reports.stream().map((Status status) -> { + return switch (status.severity()) { + case INFO -> new Message(status.message(), MessageLevel.INFO); + case WARNING -> new Message(status.message(), MessageLevel.WARNING); + case ERROR -> new Message(status.message(), MessageLevel.ERROR); + default -> null; + }; + }).filter(Objects::nonNull).toList(); + } + +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/SysideSysMLTextImporter.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/SysideSysMLTextImporter.java new file mode 100644 index 000000000..166f1c178 --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/SysideSysMLTextImporter.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.services; + +import java.io.ByteArrayInputStream; +import java.util.List; +import java.util.Objects; + +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.sirius.components.representations.Message; +import org.eclipse.sirius.components.representations.MessageLevel; +import org.eclipse.syson.sysml.ASTTransformer; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.SysmlToAst; +import org.eclipse.syson.sysml.services.api.ISysMLTextImporter; +import org.eclipse.syson.sysml.textual.utils.Status; +import org.springframework.stereotype.Service; + +/** + * syside-based implementation of {@code ISysMLTextImporter}. + * + * @author pcdavid + */ +@Service +public class SysideSysMLTextImporter implements ISysMLTextImporter { + private final SysmlToAst sysmlToAst; + + public SysideSysMLTextImporter(SysmlToAst sysmlToAst) { + this.sysmlToAst = Objects.requireNonNull(sysmlToAst); + } + + @Override + public List importSysMLText(IEMFEditingContext emfEditingContext, Element parentElement, String textualContent, List messages) { + List newObjects = List.of(); + + // Phase 1: parse the raw text into a JSON AST using syside-cli + var inputStream = new ByteArrayInputStream(textualContent.getBytes()); + var astParsingResult = this.sysmlToAst.convert(inputStream, ".sysml"); + messages.addAll(this.toMessages(astParsingResult.reports())); + + // Phase 2: convert the resulting JSON AST into actual SySMLv2 Elements and integrate them into + // parentElement. + if (astParsingResult.ast().isPresent()) { + var transformer = new ASTTransformer(); + newObjects = transformer.convertToElements(astParsingResult.ast().get(), parentElement); + messages.addAll(transformer.getTransformationMessages()); + } + + return newObjects; + } + + private List toMessages(List reports) { + return reports.stream().map((Status status) -> { + return switch (status.severity()) { + case INFO -> new Message(status.message(), MessageLevel.INFO); + case WARNING -> new Message(status.message(), MessageLevel.WARNING); + case ERROR -> new Message(status.message(), MessageLevel.ERROR); + default -> null; + }; + }).filter(Objects::nonNull).toList(); + } +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ExpressionCreationResult.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ExpressionCreationResult.java new file mode 100644 index 000000000..460e3237d --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ExpressionCreationResult.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.services.api; + +import java.util.List; + +import org.eclipse.sirius.components.representations.Message; +import org.eclipse.syson.sysml.Expression; + +/** + * The result of creating a new expression from text. {@code createdExpression} may be null to indicate the + * expression could not be created, in which case {@cope messages} will contain the reason(s) for the failure. + * + * @author pcdavid + */ +public record ExpressionCreationResult(Expression createdExpression, List messages) { +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ISysMLExpressionEditor.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ISysMLExpressionEditor.java new file mode 100644 index 000000000..e7755092c --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ISysMLExpressionEditor.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.services.api; + +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.syson.sysml.Element; +import org.eclipse.syson.sysml.Expression; + +/** + * Use to create of edit a SySMLv2 {@link Expression} in a given context from a given text fragment. + * + * @author pcdavid + */ +public interface ISysMLExpressionEditor { + ExpressionCreationResult createExpression(IEMFEditingContext emfEditingContext, Element parentElement, String expressionText); + + // "Editing" an expression actually means creating a new one to replace the previous one. + ExpressionCreationResult editExpression(IEMFEditingContext emfEditingContext, Element parentElement, Expression expression, String expressionText); +} diff --git a/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ISysMLTextImporter.java b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ISysMLTextImporter.java new file mode 100644 index 000000000..d75a1d231 --- /dev/null +++ b/backend/application/syson-sysml-import/src/main/java/org/eclipse/syson/sysml/services/api/ISysMLTextImporter.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.sysml.services.api; + +import java.util.List; + +import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; +import org.eclipse.sirius.components.representations.Message; +import org.eclipse.syson.sysml.Element; + +/** + * Use to import a SySMLv2 text fragment as new objects inside an existing element. + * + * @author pcdavid + */ +public interface ISysMLTextImporter { + List importSysMLText(IEMFEditingContext emfEditingContext, Element parentElement, String textualContent, List messages); +} diff --git a/backend/application/syson-sysml-import/src/main/resources/schema/syson-import.graphqls b/backend/application/syson-sysml-import/src/main/resources/schema/syson-import.graphqls index b49525150..a2df45eca 100644 --- a/backend/application/syson-sysml-import/src/main/resources/schema/syson-import.graphqls +++ b/backend/application/syson-sysml-import/src/main/resources/schema/syson-import.graphqls @@ -1,4 +1,3 @@ - extend type Mutation { insertTextualSysMLv2(input: InsertTextualSysMLv2Input!): InsertTextualSysMLv2Payload! } @@ -11,3 +10,38 @@ input InsertTextualSysMLv2Input { } union InsertTextualSysMLv2Payload = ErrorPayload | SuccessPayload + +extend type Mutation { + createExpression(input: CreateExpressionInput!): CreateExpressionPayload! + editExpression(input: EditExpressionInput!): EditExpressionPayload! +} + +input CreateExpressionInput { + id: ID! + editingContextId: ID! + parentElementId: ID! + expressionText: String! +} + +union CreateExpressionPayload = ErrorPayload | CreateExpressionSuccessPayload + +type CreateExpressionSuccessPayload { + id: ID! + newExpressionId: ID! + messages: [Message]! +} + +input EditExpressionInput { + id: ID! + editingContextId: ID! + expressionElementId: ID! + newExpressionText: String! +} + +union EditExpressionPayload = ErrorPayload | EditExpressionSuccessPayload + +type EditExpressionSuccessPayload { + id: ID! + newExpressionId: ID! + messages: [Message]! +} \ No newline at end of file diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/ResultExpressionMembershipImpl.java b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/ResultExpressionMembershipImpl.java index a0c09cbfb..61f233d88 100644 --- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/ResultExpressionMembershipImpl.java +++ b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/ResultExpressionMembershipImpl.java @@ -1,5 +1,5 @@ /******************************************************************************* -* Copyright (c) 2023, 2025 Obeo. +* Copyright (c) 2023, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -66,13 +66,14 @@ public Expression getOwnedResultExpression() { /** * * - * @generated + * @generated NOT */ public Expression basicGetOwnedResultExpression() { - // TODO: implement this method to return the 'Owned Result Expression' reference - // -> do not perform proxy resolution - // Ensure that you remove @generated or mark it @generated NOT - return null; + return this.getOwnedRelatedElement().stream() + .filter(Expression.class::isInstance) + .map(Expression.class::cast) + .findFirst() + .orElse(null); } /** diff --git a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelQueryElementService.java b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelQueryElementService.java index d47f02ac6..fb95485a6 100644 --- a/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelQueryElementService.java +++ b/backend/services/syson-sysml-metamodel-services/src/main/java/org/eclipse/syson/sysml/metamodel/services/MetamodelQueryElementService.java @@ -19,19 +19,30 @@ import java.util.function.Predicate; import org.eclipse.syson.sysml.ActorMembership; +import org.eclipse.syson.sysml.Behavior; import org.eclipse.syson.sysml.BooleanExpression; import org.eclipse.syson.sysml.ConcernUsage; import org.eclipse.syson.sysml.Connector; +import org.eclipse.syson.sysml.ConstructorExpression; import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.Expression; import org.eclipse.syson.sysml.Feature; import org.eclipse.syson.sysml.FeatureValue; import org.eclipse.syson.sysml.FramedConcernMembership; +import org.eclipse.syson.sysml.Function; +import org.eclipse.syson.sysml.Namespace; +import org.eclipse.syson.sysml.OwningMembership; import org.eclipse.syson.sysml.PartUsage; import org.eclipse.syson.sysml.ReferenceUsage; +import org.eclipse.syson.sysml.ResultExpressionMembership; import org.eclipse.syson.sysml.StakeholderMembership; +import org.eclipse.syson.sysml.Step; import org.eclipse.syson.sysml.SubjectMembership; +import org.eclipse.syson.sysml.SysmlFactory; import org.eclipse.syson.sysml.Usage; +import org.eclipse.syson.sysml.textual.SysMLElementSerializer; +import org.eclipse.syson.sysml.textual.SysMLSerializingOptions; +import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver; /** * Element-related services doing queries. This class should not depend on sirius-web services or other spring services. @@ -120,6 +131,124 @@ public boolean isTopLevelExpression(Element element) { return result; } + /** + * Check is a given {@code element} has a single/non-ambiguous existing {@link Expression} definition associated. + * + * @param element + * the element to test + * @return true if the element has a single existing associated expression definition. + */ + public boolean hasSingleExpressionDefinition(Element element) { + return this.findSingleExpressionDefinition(element).isPresent(); + } + + /** + * Check whether a given {@link Element element} can contain an {@link Expression}. + * + * @param element + * the element to test. + * @return true if the element can contain an expression. + */ + public boolean canContainExpressionDefinition(Element element) { + return !this.getCompatibleExpressionOwnerships(element).isEmpty(); + } + + /** + * Given an {@link Element element}, returns the list of all {@link OwningMembership owning memberships} through + * which it can contain an {@link Expression expression}. + * + * @param element + * a SysML {@link Element element}. + * @return the owning memberships through which the element may contain an expression. + */ + public List getCompatibleExpressionOwnerships(Element element) { + var result = new ArrayList(); + + // KerML 8.3.4.6.4 ParameterMembership: "A ParameterMembership must be owned by a Behavior, a Step, or the + // result parameter of a ConstructorExpression." + if (element instanceof Behavior || element instanceof Step || element instanceof ConstructorExpression) { + result.add(SysmlFactory.eINSTANCE.createParameterMembership()); + } + + // KerML 8.3.4.7.7 ResultExpressionMembership: "A ResultExpressionMembership is a FeatureMembership that + // indicates that the ownedResultExpression provides the result values for the Function or Expression that owns + // it. The owning Function or Expression must contain a BindingConnector between the result parameter of the + // ownedResultExpression and the result parameter of the owning Function or Expression." + if (element instanceof Function || element instanceof Expression) { + result.add(SysmlFactory.eINSTANCE.createResultExpressionMembership()); + } + + // KerML 8.3.4.10.2 FeatureValue: "A FeatureValue is a Membership that identifies a particular member + // Expression that provides the value of the Feature that owns the FeatureValue." + if (element instanceof Feature) { + result.add(SysmlFactory.eINSTANCE.createFeatureValue()); + } + + return result; + } + + /** + * Check is a given {@code element} has a single/non-ambiguous existing {@link Expression} definition associated. + * + * @param element + * the element to test + * @return true if the element has a single existing associated expression definition. + */ + public Optional findSingleExpressionDefinition(Element element) { + Optional result = Optional.empty(); + if (this.isExpressionDefinition(element)) { + result = Optional.of((Expression) element); + } else { + var ownedExpressions = element.getOwnedElement().stream().filter(child -> this.isExpressionDefinition(child)).toList(); + if (ownedExpressions.size() == 1) { + result = Optional.of((Expression) ownedExpressions.get(0)); + } + } + return result; + } + + + /** + * Get the {@link ResultExpressionMembership} contained inside a given {@link Namespace}. + * + * @param namespace + * a given {@link Namespace}. + * @return a {@link ResultExpressionMembership}, or null if not found. + */ + public ResultExpressionMembership getResultExpressionMembership(Namespace namespace) { + return namespace.getOwnedMembership().stream() + .filter(ResultExpressionMembership.class::isInstance) + .map(ResultExpressionMembership.class::cast) + .findFirst() + .orElse(null); + } + + /** + * Returns the textual representation of an {@link Expression expression}. + * + * @param expression + * an expression. + * @return its textual representation/serialization. + */ + public String getExpressionTextualRepresentation(Expression expression) { + String result = ""; + if (expression != null) { + SysMLSerializingOptions options = new SysMLSerializingOptions.Builder() + .lineSeparator("\n") + .nameDeresolver(new FileNameDeresolver()) + .indentation("\t") + .needEscapeCharacter(false) + .build(); + String textualFormat = new SysMLElementSerializer(options, s -> { + // Do nothing for now + }).doSwitch(expression); + if (textualFormat != null) { + result = textualFormat; + } + } + return result; + } + /** * Get the source of a {@link Connector}. * diff --git a/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java b/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java index 906e39d7b..eaf370209 100644 --- a/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java +++ b/backend/services/syson-tree-services/src/main/java/org/eclipse/syson/tree/explorer/services/SysONDefaultExplorerServices.java @@ -42,9 +42,6 @@ import org.eclipse.syson.sysml.Type; import org.eclipse.syson.sysml.ViewUsage; import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; -import org.eclipse.syson.sysml.textual.SysMLElementSerializer; -import org.eclipse.syson.sysml.textual.SysMLSerializingOptions; -import org.eclipse.syson.sysml.textual.utils.FileNameDeresolver; import org.eclipse.syson.sysml.util.ElementUtil; import org.eclipse.syson.tree.explorer.fragments.KerMLStandardLibraryDirectory; import org.eclipse.syson.tree.explorer.fragments.LibrariesDirectory; @@ -157,7 +154,7 @@ public String getLabel(Object self) { if (self instanceof ISysONExplorerFragment fragment) { label = fragment.getLabel(); } else if (self instanceof Expression expression && this.metamodelQueryElementService.isExpressionDefinition(expression)) { - label = this.getValueExpressionTextualRepresentation(expression); + label = this.metamodelQueryElementService.getExpressionTextualRepresentation(expression); } else if (self instanceof Type type) { String name = type.getName(); if (name != null) { @@ -169,25 +166,6 @@ public String getLabel(Object self) { return label; } - private String getValueExpressionTextualRepresentation(Expression value) { - String result = ""; - if (value != null) { - SysMLSerializingOptions options = new SysMLSerializingOptions.Builder() - .lineSeparator("\n") - .nameDeresolver(new FileNameDeresolver()) - .indentation("\t") - .needEscapeCharacter(false) - .build(); - String textualFormat = new SysMLElementSerializer(options, s -> { - // Do nothing for now - }).doSwitch(value); - if (textualFormat != null) { - result = textualFormat; - } - } - return result; - } - private String getFallbackLabel(Object self) { StyledString styledLabel = this.labelService.getStyledLabel(self); if (styledLabel != null) { @@ -264,7 +242,7 @@ public boolean canExpandAll(TreeItem treeItem, IEditingContext editingContext) { @Override public boolean canCreateNewObjectsFromText(Object self) { - return self instanceof Element && this.isEditable(self); + return self instanceof Element element && !this.readOnlyObjectPredicate.test(element); } @Override @@ -303,7 +281,7 @@ public boolean isEditable(Object self) { if (self instanceof ISysONExplorerFragment fragment) { result = fragment.isEditable(); } else if (self instanceof Element element) { - result = !this.readOnlyObjectPredicate.test(element); + result = !this.readOnlyObjectPredicate.test(element) && !this.metamodelQueryElementService.isExpressionDefinition(element); } else if (self instanceof Resource resource) { result = !this.readOnlyObjectPredicate.test(resource); } diff --git a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java index ac4f90efe..9e4dee378 100644 --- a/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java +++ b/backend/views/syson-tree-explorer-view/src/main/java/org/eclipse/syson/tree/explorer/view/menu/context/SysONExplorerTreeItemContextMenuEntryProvider.java @@ -27,6 +27,7 @@ import org.eclipse.sirius.components.collaborative.trees.dto.SingleClickTreeItemContextMenuEntry; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IObjectSearchService; +import org.eclipse.sirius.components.core.api.IReadOnlyObjectPredicate; import org.eclipse.sirius.components.emf.services.api.IEMFEditingContext; import org.eclipse.sirius.components.trees.Tree; import org.eclipse.sirius.components.trees.TreeItem; @@ -43,6 +44,7 @@ import org.eclipse.syson.sysml.Element; import org.eclipse.syson.sysml.Relationship; import org.eclipse.syson.sysml.ViewUsage; +import org.eclipse.syson.sysml.metamodel.services.MetamodelQueryElementService; import org.eclipse.syson.tree.explorer.services.api.ISysONExplorerService; import org.eclipse.syson.tree.explorer.view.SysONTreeViewDescriptionProvider; import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers; @@ -60,6 +62,12 @@ public class SysONExplorerTreeItemContextMenuEntryProvider implements ITreeItemC public static final String NEW_OBJECTS_FROM_TEXT_MENU_ENTRY_CONTRIBUTION_ID = "newObjectsFromText"; + public static final String CREATE_EXPRESSION_MENU_ENTRY_CONTRIBUTION_ID = "createExpression"; + + public static final String EDIT_EXPRESSION_MENU_ENTRY_CONTRIBUTION_ID = "editExpression"; + + public static final String DELETE_EXPRESSION_MENU_ENTRY_CONTRIBUTION_ID = "deleteExpression"; + private final IObjectSearchService objectSearchService; private final ILibrarySearchService librarySearchService; @@ -72,15 +80,21 @@ public class SysONExplorerTreeItemContextMenuEntryProvider implements ITreeItemC private final ISysONExplorerService sysonExplorerService; + private final IReadOnlyObjectPredicate readOnlyObjectPredicate; + + private final MetamodelQueryElementService metamodelQueryElementService; + public SysONExplorerTreeItemContextMenuEntryProvider(IObjectSearchService objectSearchService, ILibrarySearchService librarySearchService, ISemanticDataSearchService semanticDataSearchService, IRepresentationMetadataSearchService representationMetadataSearchService, - SysONTreeViewDescriptionProvider sysONTreeViewDescriptionProvider, ISysONExplorerService sysonExplorerService) { + SysONTreeViewDescriptionProvider sysONTreeViewDescriptionProvider, ISysONExplorerService sysonExplorerService, IReadOnlyObjectPredicate readOnlyObjectPredicate) { this.objectSearchService = Objects.requireNonNull(objectSearchService); this.librarySearchService = Objects.requireNonNull(librarySearchService); this.semanticDataSearchService = Objects.requireNonNull(semanticDataSearchService); this.representationMetadataSearchService = Objects.requireNonNull(representationMetadataSearchService); this.sysONTreeViewDescriptionProvider = Objects.requireNonNull(sysONTreeViewDescriptionProvider); this.sysonExplorerService = Objects.requireNonNull(sysonExplorerService); + this.readOnlyObjectPredicate = Objects.requireNonNull(readOnlyObjectPredicate); + this.metamodelQueryElementService = new MetamodelQueryElementService(); } @Override @@ -122,38 +136,70 @@ private List getDocumentContextMenuEntries(IEMFEditin } private List getObjectContextMenuEntries(IEMFEditingContext editingContext, TreeItem treeItem) { + List entries = new ArrayList<>(); var optionalEObject = this.objectSearchService.getObject(editingContext, treeItem.getId()) .filter(EObject.class::isInstance) .map(EObject.class::cast); if (optionalEObject.isPresent()) { var object = optionalEObject.get(); if (this.sysonExplorerService.isEditable(object)) { - List entries = new ArrayList<>(); entries.add(new SingleClickTreeItemContextMenuEntry(ExplorerTreeItemContextMenuEntryProvider.NEW_OBJECT, "", List.of(), false, List.of())); - if (object instanceof ViewUsage) { - Optional semanticDataId = new UUIDParser().parse(editingContext.getId()); - var sysonRepresentationAlreadyExists = this.representationMetadataSearchService - .findAllRepresentationMetadataBySemanticDataAndTargetObjectId(AggregateReference.to(semanticDataId.get()), treeItem.getId()).stream() - .anyMatch(rm -> SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID.equals(rm.getDescriptionId()) - || SysONRepresentationDescriptionIdentifiers.REQUIREMENTS_TABLE_VIEW_DESCRIPTION_ID.equals(rm.getDescriptionId())); - if (!sysonRepresentationAlreadyExists) { - entries.add(new SingleClickTreeItemContextMenuEntry(ExplorerTreeItemContextMenuEntryProvider.NEW_REPRESENTATION, "", List.of(), false, List.of())); - } - } else { + if (this.canHaveNewRepresentation(editingContext, treeItem.getId(), object)) { entries.add(new SingleClickTreeItemContextMenuEntry(ExplorerTreeItemContextMenuEntryProvider.NEW_REPRESENTATION, "", List.of(), false, List.of())); } - entries.add(new SingleClickTreeItemContextMenuEntry(NEW_OBJECTS_FROM_TEXT_MENU_ENTRY_CONTRIBUTION_ID, "", List.of(), false, List.of())); + if (this.canHaveNewExpression(editingContext, treeItem.getId(), object)) { + entries.add(new SingleClickTreeItemContextMenuEntry(CREATE_EXPRESSION_MENU_ENTRY_CONTRIBUTION_ID, "", List.of(), false, List.of())); + } if (object instanceof Element && !(object instanceof Relationship)) { entries.add(new SingleClickTreeItemContextMenuEntry(ExplorerTreeItemContextMenuEntryProvider.DUPLICATE_OBJECT, "", List.of(), false, List.of())); } - return entries; + } + if (!this.readOnlyObjectPredicate.test(object) && object instanceof Element element) { + this.addExpressionEditionEntries(entries, element); } } - return List.of(); + return entries; + } + + private boolean canHaveNewRepresentation(IEMFEditingContext editingContext, String treeItemId, EObject object) { + boolean result = true; + if (object instanceof ViewUsage) { + Optional semanticDataId = new UUIDParser().parse(editingContext.getId()); + var sysonRepresentationAlreadyExists = this.representationMetadataSearchService + .findAllRepresentationMetadataBySemanticDataAndTargetObjectId(AggregateReference.to(semanticDataId.get()), treeItemId).stream() + .anyMatch(rm -> SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID.equals(rm.getDescriptionId()) + || SysONRepresentationDescriptionIdentifiers.REQUIREMENTS_TABLE_VIEW_DESCRIPTION_ID.equals(rm.getDescriptionId())); + if (sysonRepresentationAlreadyExists) { + result = false; + } + } + return result; } + private boolean canHaveNewExpression(IEMFEditingContext editingContext, String treeItemId, EObject object) { + return !this.readOnlyObjectPredicate.test(object) && object instanceof Element element && this.metamodelQueryElementService.canContainExpressionDefinition(element) + && !this.metamodelQueryElementService.hasSingleExpressionDefinition(element); + } + + private void addExpressionEditionEntries(List entries, Element element) { + var expressionEntries = new ArrayList(); + + // "Edit expression" on the root Expression element itself; the normal "Delete" operation works on it so no need + // to also add "Delete expression" + if (this.metamodelQueryElementService.isTopLevelExpression(element)) { + expressionEntries.add(EDIT_EXPRESSION_MENU_ENTRY_CONTRIBUTION_ID); + } else if (this.metamodelQueryElementService.hasSingleExpressionDefinition(element) + && !this.metamodelQueryElementService.hasSingleExpressionDefinition(element.getOwner())) { + // "Delete expression" on the owner of a root Expression element + expressionEntries.add(DELETE_EXPRESSION_MENU_ENTRY_CONTRIBUTION_ID); + } + + expressionEntries.forEach(id -> entries.add(new SingleClickTreeItemContextMenuEntry(id, "", List.of(), false, List.of()))); + } + + private List getRepresentationContextMenuEntries(IEMFEditingContext editingContext, TreeItem treeItem) { var optionalRepresentationMetadata = this.objectSearchService.getObject(editingContext, treeItem.getId()) .filter(RepresentationMetadata.class::isInstance) diff --git a/frontend/syson-components/src/extensions/NewObjectAsTextDocumentReport.tsx b/frontend/syson-components/src/extensions/NewObjectAsTextDocumentReport.tsx index 480a44be4..59a2cfac2 100644 --- a/frontend/syson-components/src/extensions/NewObjectAsTextDocumentReport.tsx +++ b/frontend/syson-components/src/extensions/NewObjectAsTextDocumentReport.tsx @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at diff --git a/frontend/syson-components/src/extensions/SysONExtensionRegistryMergeStrategy.ts b/frontend/syson-components/src/extensions/SysONExtensionRegistryMergeStrategy.ts index f2dfab167..4bc38fe67 100644 --- a/frontend/syson-components/src/extensions/SysONExtensionRegistryMergeStrategy.ts +++ b/frontend/syson-components/src/extensions/SysONExtensionRegistryMergeStrategy.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2024, 2025 Obeo. + * Copyright (c) 2024, 2026 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at diff --git a/frontend/syson-components/src/extensions/expressions/DeleteSysMLExpressionMenuContribution.tsx b/frontend/syson-components/src/extensions/expressions/DeleteSysMLExpressionMenuContribution.tsx new file mode 100644 index 000000000..605e6afa3 --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/DeleteSysMLExpressionMenuContribution.tsx @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { useDeletionConfirmationDialog } from '@eclipse-sirius/sirius-components-core'; +import { TreeItemContextMenuComponentProps } from '@eclipse-sirius/sirius-components-trees'; +import DeleteIcon from '@mui/icons-material/Delete'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import MenuItem from '@mui/material/MenuItem'; +import React, { forwardRef, Fragment } from 'react'; +import { useDeleteExpression } from './useDeleteExpression'; + +export const DeleteSysMLExpressionMenuContribution = forwardRef( + ( + { editingContextId, treeId, item, readOnly, onClose }: TreeItemContextMenuComponentProps, + ref: React.ForwardedRef + ) => { + const { deleteExpression } = useDeleteExpression(); + const { showDeletionConfirmation } = useDeletionConfirmationDialog(); + + if (!treeId.startsWith('explorer://') || readOnly) { + return null; + } + + const handleDeleteExpression = () => { + showDeletionConfirmation(() => { + deleteExpression(editingContextId, item.id); + onClose(); + }); + }; + + return ( + + + + + + + + + ); + } +); diff --git a/frontend/syson-components/src/extensions/expressions/EditSysMLExpressionMenuContribution.tsx b/frontend/syson-components/src/extensions/expressions/EditSysMLExpressionMenuContribution.tsx new file mode 100644 index 000000000..d6827cb06 --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/EditSysMLExpressionMenuContribution.tsx @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { TreeItemContextMenuComponentProps } from '@eclipse-sirius/sirius-components-trees'; +import EditIcon from '@mui/icons-material/Edit'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import MenuItem from '@mui/material/MenuItem'; +import React, { forwardRef, Fragment, useState } from 'react'; +import { EditSysMLExpressionModal } from './EditSysMLExpressionModal'; + +export const EditSysMLExpressionMenuContribution = forwardRef( + ( + { editingContextId, treeId, item, readOnly, onClose }: TreeItemContextMenuComponentProps, + ref: React.ForwardedRef + ) => { + const [modalOpened, setModalOpened] = useState(false); + + const onCloseModal = () => { + setModalOpened(false); + onClose(); + }; + + if (!treeId.startsWith('explorer://')) { + return null; + } + + let modalElement: JSX.Element | null = null; + if (modalOpened === true) { + modalElement = ( + + ); + } + + return ( + + setModalOpened(true)} + data-testid="edit-sysml-expression-menu" + disabled={readOnly} + ref={ref} + aria-disabled> + + + + + + {modalElement} + + ); + } +); diff --git a/frontend/syson-components/src/extensions/expressions/EditSysMLExpressionModal.tsx b/frontend/syson-components/src/extensions/expressions/EditSysMLExpressionModal.tsx new file mode 100644 index 000000000..4e6c4a3fa --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/EditSysMLExpressionModal.tsx @@ -0,0 +1,319 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +import { GQLMessage } from '@eclipse-sirius/sirius-components-core'; +import CheckCircleOutlinedIcon from '@mui/icons-material/CheckCircleOutlined'; +import ErrorOutlineOutlinedIcon from '@mui/icons-material/ErrorOutlineOutlined'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import NotesOutlinedIcon from '@mui/icons-material/NotesOutlined'; +import Accordion from '@mui/material/Accordion'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import Button from '@mui/material/Button'; +import Chip from '@mui/material/Chip'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import InputAdornment from '@mui/material/InputAdornment'; +import { Theme } from '@mui/material/styles'; +import TextField from '@mui/material/TextField'; +import Typography from '@mui/material/Typography'; +import { useEffect, useState } from 'react'; +import { makeStyles } from 'tss-react/mui'; +import { EditSysMLExpressionModalProps, EditSysMLExpressionModalState } from './EditSysMLExpressionModal.types'; +import { useCreateExpression } from './useCreateExpression'; +import { useDeleteExpression } from './useDeleteExpression'; +import { useEditExpression } from './useEditExpression'; +import { useExpressionTextualRepresentation } from './useExpressionTextualRepresentation'; + +const useEditSysMLExpressionModalStyles = makeStyles()((theme: Theme) => ({ + form: { + display: 'flex', + flexDirection: 'column', + gap: theme.spacing(2), + '& > *': { + marginBottom: theme.spacing(1), + }, + }, + textarea: { + flexGrow: 1, + }, + feedback: { + display: 'flex', + flexDirection: 'column', + }, + actions: { + marginRight: theme.spacing(2), + marginBottom: theme.spacing(1), + }, + adornment: { + alignSelf: 'flex-end', + }, + errorAccordion: { + backgroundColor: '#FCE9E680', + }, + status: { + flexGrow: 1, + marginLeft: '16px', + }, +})); + +type ValidationStatus = 'unknown' | 'valid' | 'invalid'; + +const computeValidationStatus = (validationResult: GQLMessage[] | null): ValidationStatus => { + if (validationResult === null) { + return 'unknown'; + } else { + const errors = validationResult.filter((message) => message.level !== 'INFO' && message.level !== 'SUCCESS'); + return errors.length === 0 ? 'valid' : 'invalid'; + } +}; + +export const EditSysMLExpressionModal = ({ + editingContextId, + elementId, + mode, + onClose, +}: EditSysMLExpressionModalProps) => { + const { classes } = useEditSysMLExpressionModalStyles(); + + const [state, setState] = useState({ + textualContent: null, + operationInProgress: null, + validationResult: null, + }); + const validationStatus = computeValidationStatus(state.validationResult); + const busy = state.operationInProgress !== null; + + const { textualRepresentation, loading } = useExpressionTextualRepresentation(editingContextId, elementId); + useEffect(() => { + if (loading) { + setState((prevState) => ({ ...prevState, operationInProgress: 'loading' })); + } else { + setState((prevState) => ({ + ...prevState, + operationInProgress: null, + textualContent: textualRepresentation, + validationResult: null, + })); + } + }, [textualRepresentation, loading]); + + const { createExpression, loading: creationInProgress, messages: postCreationMessages } = useCreateExpression(); + const { editExpression, loading: editionInProgress, messages: postEditionMessages } = useEditExpression(); + const { deleteExpression } = useDeleteExpression(); + + const onUpdate = (event: React.MouseEvent) => { + event.preventDefault(); + if (state.textualContent !== null) { + if (mode === 'create' && state.textualContent.trim() !== '') { + setState((prevState) => ({ ...prevState, operationInProgress: 'updating' })); + createExpression(editingContextId, elementId, state.textualContent); + } else if (mode === 'edit' && state.textualContent !== null) { + if (state.textualContent.trim() === '') { + deleteExpression(editingContextId, elementId); + onClose(); + } else { + setState((prevState) => ({ ...prevState, operationInProgress: 'updating' })); + editExpression(editingContextId, elementId, state.textualContent); + } + } + } + }; + + // Update validationResult when the operation is finished + useEffect(() => { + if (state.operationInProgress === 'updating' && !creationInProgress) { + setState((prevState) => ({ ...prevState, operationInProgress: null, validationResult: postCreationMessages })); + } + }, [state.operationInProgress, creationInProgress, postCreationMessages]); + useEffect(() => { + if (state.operationInProgress === 'updating' && !editionInProgress) { + setState((prevState) => ({ ...prevState, operationInProgress: null, validationResult: postEditionMessages })); + } + }, [state.operationInProgress, editionInProgress, postEditionMessages]); + + // Closing if we have an explicit valid result + useEffect(() => { + if (validationStatus === 'valid') { + onClose(); + } + }, [validationStatus, onClose]); + + return ( + onClose()} + aria-labelledby="dialog-title" + maxWidth="md" + fullWidth + data-testid="edit-sysml-expression-modal" + onKeyDown={(e) => e.stopPropagation()}> + {mode === 'create' ? 'Create Expression' : 'Edit Expression'} + +
+ + Enter a valid expression below + + + setState((prevState) => ({ ...prevState, textualContent: event.target.value, validationResult: null })) + } + slotProps={{ + input: { + endAdornment: , + }, + }} + /> +
+ {validationStatus === 'invalid' && state.validationResult ? ( + + }> + + {state.validationResult.length + ' error' + (state.validationResult.length > 1 ? 's' : '')} + + + + {state.validationResult.map((error, index) => ( + + {index + 1 + '. ' + error.body} + + ))} + + + ) : null} +
+
+
+ +
+ +
+ + +
+
+ ); +}; + +const useExpressionStatusStyles = makeStyles()((theme: Theme) => ({ + statusSummary: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + gap: theme.spacing(1), + }, +})); + +type ExpressionStatusProps = { + blank: boolean; + validationStatus: ValidationStatus; +}; + +const ExpressionStatus = ({ blank, validationStatus }: ExpressionStatusProps) => { + const { classes } = useExpressionStatusStyles(); + if (blank) { + return ( +
+ + + Expression is blank, it will be deleted if you click on update + +
+ ); + } + switch (validationStatus) { + case 'valid': + return ( +
+ + + Valid + +
+ ); + case 'invalid': + return ( +
+ + + Invalid expression, please check the syntax + +
+ ); + default: + return ( +
+ + + Expression will be validated when you click on update + +
+ ); + } +}; + +const chipFeedbackColor = (validationStatus: ValidationStatus): 'success' | 'error' | 'default' => { + switch (validationStatus) { + case 'valid': + return 'success'; + case 'invalid': + return 'error'; + default: + return 'default'; + } +}; + +const SysMLStatusChip = ({ validationStatus }: { validationStatus: ValidationStatus }) => { + const color = chipFeedbackColor(validationStatus); + const iconColor = color === 'default' ? 'disabled' : color; + return ( + + } + size="small" + variant="outlined" + label="SysML" + /> + + ); +}; diff --git a/frontend/syson-components/src/extensions/expressions/EditSysMLExpressionModal.types.ts b/frontend/syson-components/src/extensions/expressions/EditSysMLExpressionModal.types.ts new file mode 100644 index 000000000..69ac824b3 --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/EditSysMLExpressionModal.types.ts @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { GQLMessage } from '@eclipse-sirius/sirius-components-core'; + +export type Mode = 'create' | 'edit'; + +export interface EditSysMLExpressionModalProps { + editingContextId: string; + elementId: string; + mode: Mode; + onClose: () => void; +} + +export type Operation = 'loading' | 'updating'; + +export interface EditSysMLExpressionModalState { + operationInProgress: Operation | null; + textualContent: string | null; + validationResult: GQLMessage[] | null; +} diff --git a/frontend/syson-components/src/extensions/expressions/NewSysMLExpressionMenuContribution.tsx b/frontend/syson-components/src/extensions/expressions/NewSysMLExpressionMenuContribution.tsx new file mode 100644 index 000000000..0dbe6dc6c --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/NewSysMLExpressionMenuContribution.tsx @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { TreeItemContextMenuComponentProps } from '@eclipse-sirius/sirius-components-trees'; +import AddIcon from '@mui/icons-material/Add'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import ListItemText from '@mui/material/ListItemText'; +import MenuItem from '@mui/material/MenuItem'; +import React, { forwardRef, Fragment, useState } from 'react'; +import { EditSysMLExpressionModal } from './EditSysMLExpressionModal'; + +export const NewSysMLExpressionMenuContribution = forwardRef( + ( + { editingContextId, treeId, item, readOnly, onClose }: TreeItemContextMenuComponentProps, + ref: React.ForwardedRef + ) => { + const [modalOpened, setModalOpened] = useState(false); + + const onCloseModal = () => { + setModalOpened(false); + onClose(); + }; + + if (!treeId.startsWith('explorer://') || readOnly) { + return null; + } + + let modalElement: JSX.Element | null = null; + if (modalOpened === true) { + modalElement = ( + + ); + } + + return ( + + setModalOpened(true)} + data-testid="new-sysml-expression-menu" + disabled={readOnly} + ref={ref} + aria-disabled> + + + + + + {modalElement} + + ); + } +); diff --git a/frontend/syson-components/src/extensions/expressions/useCreateExpression.ts b/frontend/syson-components/src/extensions/expressions/useCreateExpression.ts new file mode 100644 index 000000000..21b51e101 --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/useCreateExpression.ts @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +import { gql, useMutation } from '@apollo/client'; +import { GQLMessage } from '@eclipse-sirius/sirius-components-core'; +import { useEffect, useState } from 'react'; +import { + GQLCreateExpressionData, + GQLCreateExpressionInput, + GQLCreateExpressionPayload, + GQLCreateExpressionSuccessPayload, + GQLCreateExpressionVariables, + GQLErrorPayload, + UseCreateExpressionValue, +} from './useCreateExpression.types'; + +const createExpressionMutation = gql` + mutation createExpression($input: CreateExpressionInput!) { + createExpression(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on CreateExpressionSuccessPayload { + newExpressionId + messages { + body + level + } + } + } + } +`; + +const isErrorPayload = (payload: GQLCreateExpressionPayload): payload is GQLErrorPayload => + payload.__typename === 'ErrorPayload'; +const isSuccessPayload = (payload: GQLCreateExpressionPayload): payload is GQLCreateExpressionSuccessPayload => + payload.__typename === 'CreateExpressionSuccessPayload'; + +export const useCreateExpression = (): UseCreateExpressionValue => { + const [messages, setMessages] = useState([]); + const [newExpressionId, setNewExpressionId] = useState(null); + + const [performCreateExpression, { loading, data, error }] = useMutation< + GQLCreateExpressionData, + GQLCreateExpressionVariables + >(createExpressionMutation); + + useEffect(() => { + if (error) { + setMessages([{ body: 'An unexpected error has occurred, please refresh the page', level: 'ERROR' }]); + } + if (data) { + const { createExpression } = data; + setMessages(createExpression.messages); + if (isErrorPayload(createExpression)) { + setNewExpressionId(null); + } + if (isSuccessPayload(createExpression)) { + setNewExpressionId(createExpression.newExpressionId); + } + } + }, [data, error]); + + const createExpression = (editingContextId: string, parentElementId: string, expressionText: string) => { + const input: GQLCreateExpressionInput = { + id: crypto.randomUUID(), + editingContextId, + parentElementId, + expressionText, + }; + performCreateExpression({ variables: { input } }); + }; + + return { + createExpression, + loading, + newExpressionId, + messages, + }; +}; diff --git a/frontend/syson-components/src/extensions/expressions/useCreateExpression.types.ts b/frontend/syson-components/src/extensions/expressions/useCreateExpression.types.ts new file mode 100644 index 000000000..933d0841e --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/useCreateExpression.types.ts @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +import { GQLMessage } from '@eclipse-sirius/sirius-components-core'; + +export interface UseCreateExpressionValue { + createExpression: (editingContextId: string, parentElementId: string, expressionText: string) => void; + messages: GQLMessage[]; + newExpressionId: string | null; + loading: boolean; +} + +export interface GQLCreateExpressionVariables { + input: GQLCreateExpressionInput; +} + +export interface GQLCreateExpressionInput { + id: string; + editingContextId: string; + parentElementId: string; + expressionText: string; +} + +export interface GQLCreateExpressionData { + createExpression: GQLCreateExpressionPayload; +} + +export interface GQLCreateExpressionPayload { + __typename: string; + messages: GQLMessage[]; +} + +export interface GQLErrorPayload extends GQLCreateExpressionPayload {} + +export interface GQLCreateExpressionSuccessPayload extends GQLCreateExpressionPayload { + newExpressionId: string; +} diff --git a/frontend/syson-components/src/extensions/expressions/useDeleteExpression.ts b/frontend/syson-components/src/extensions/expressions/useDeleteExpression.ts new file mode 100644 index 000000000..79c911534 --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/useDeleteExpression.ts @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { gql, useMutation } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { useEffect } from 'react'; +import { + GQLDeleteExpressionData, + GQLDeleteExpressionInput, + GQLDeleteExpressionPayload, + GQLDeleteExpressionVariables, + GQLErrorPayload, + UseDeleteExpressionValue, +} from './useDeleteExpression.types'; + +const deleteExpressionMutation = gql` + mutation deleteExpression($input: DeleteExpressionInput!) { + deleteExpression(input: $input) { + __typename + ... on ErrorPayload { + message + } + } + } +`; + +const isErrorPayload = (payload: GQLDeleteExpressionPayload): payload is GQLErrorPayload => + payload.__typename === 'ErrorPayload'; + +export const useDeleteExpression = (): UseDeleteExpressionValue => { + const [performDeleteExpression, { data, error }] = useMutation( + deleteExpressionMutation + ); + + const { addErrorMessage } = useMultiToast(); + useEffect(() => { + if (error) { + addErrorMessage('An error has occurred while executing this action, please contact the server administrator'); + } + if (data) { + const { deleteExpression } = data; + if (isErrorPayload(deleteExpression)) { + addErrorMessage(deleteExpression.message); + } + } + }, [error, data]); + + const deleteExpression = (editingContextId: string, parentElementId: string) => { + const input: GQLDeleteExpressionInput = { + id: crypto.randomUUID(), + editingContextId, + parentElementId, + }; + + performDeleteExpression({ variables: { input } }); + }; + + return { + deleteExpression, + }; +}; diff --git a/frontend/syson-components/src/extensions/expressions/useDeleteExpression.types.ts b/frontend/syson-components/src/extensions/expressions/useDeleteExpression.types.ts new file mode 100644 index 000000000..44e79b8ed --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/useDeleteExpression.types.ts @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +export interface UseDeleteExpressionValue { + deleteExpression: (editingContextId: string, elementId: string) => void; +} + +export interface GQLDeleteExpressionData { + deleteExpression: GQLDeleteExpressionPayload; +} + +export interface GQLDeleteExpressionPayload { + __typename: string; +} + +export interface GQLDeleteExpressionVariables { + input: GQLDeleteExpressionInput; +} + +export interface GQLDeleteExpressionInput { + id: string; + editingContextId: string; + parentElementId: string; +} + +export interface GQLSuccessPayload extends GQLDeleteExpressionPayload { + id: string; +} + +export interface GQLErrorPayload extends GQLDeleteExpressionPayload { + message: string; +} diff --git a/frontend/syson-components/src/extensions/expressions/useEditExpression.ts b/frontend/syson-components/src/extensions/expressions/useEditExpression.ts new file mode 100644 index 000000000..71059da6e --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/useEditExpression.ts @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2025, 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { gql, useMutation } from '@apollo/client'; +import { GQLMessage } from '@eclipse-sirius/sirius-components-core'; +import { useEffect, useState } from 'react'; +import { + GQLEditExpressionData, + GQLEditExpressionInput, + GQLEditExpressionPayload, + GQLEditExpressionSuccessPayload, + GQLEditExpressionVariables, + GQLErrorPayload, + UseEditExpressionValue, +} from './useEditExpression.types'; + +const editExpressionMutation = gql` + mutation editExpression($input: EditExpressionInput!) { + editExpression(input: $input) { + __typename + ... on ErrorPayload { + messages { + body + level + } + } + ... on EditExpressionSuccessPayload { + newExpressionId + messages { + body + level + } + } + } + } +`; + +const isErrorPayload = (payload: GQLEditExpressionPayload): payload is GQLErrorPayload => + payload.__typename === 'ErrorPayload'; +const isSuccessPayload = (payload: GQLEditExpressionPayload): payload is GQLEditExpressionSuccessPayload => + payload.__typename === 'EditExpressionSuccessPayload'; + +export const useEditExpression = (): UseEditExpressionValue => { + const [messages, setMessages] = useState([]); + const [newExpressionId, setNewExpressionId] = useState(null); + + const [performEditExpression, { loading, data, error }] = useMutation< + GQLEditExpressionData, + GQLEditExpressionVariables + >(editExpressionMutation); + + useEffect(() => { + if (error) { + setMessages([{ body: 'An unexpected error has occurred, please refresh the page', level: 'ERROR' }]); + } + if (data) { + const { editExpression } = data; + setMessages(editExpression.messages); + if (isErrorPayload(editExpression)) { + setNewExpressionId(null); + } + if (isSuccessPayload(editExpression)) { + setNewExpressionId(editExpression.newExpressionId); + } + } + }, [data, error]); + + const editExpression = (editingContextId: string, expressionElementId: string, newExpressionText: string) => { + const input: GQLEditExpressionInput = { + id: crypto.randomUUID(), + editingContextId, + expressionElementId, + newExpressionText, + }; + + performEditExpression({ variables: { input } }); + }; + + return { + editExpression, + loading, + newExpressionId, + messages, + }; +}; diff --git a/frontend/syson-components/src/extensions/expressions/useEditExpression.types.ts b/frontend/syson-components/src/extensions/expressions/useEditExpression.types.ts new file mode 100644 index 000000000..497d8501f --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/useEditExpression.types.ts @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { GQLMessage } from '@eclipse-sirius/sirius-components-core'; + +export interface UseEditExpressionValue { + editExpression: (editingContextId: string, elementId: string, newValue: string) => void; + loading: boolean; + newExpressionId: string | null; + messages: GQLMessage[]; +} + +export interface GQLEditExpressionVariables { + input: GQLEditExpressionInput; +} + +export interface GQLEditExpressionInput { + id: string; + editingContextId: string; + expressionElementId: string; + newExpressionText: string; +} + +export interface GQLEditExpressionData { + editExpression: GQLEditExpressionPayload; +} + +export interface GQLEditExpressionPayload { + __typename: string; + messages: GQLMessage[]; +} + +export interface GQLErrorPayload extends GQLEditExpressionPayload {} + +export interface GQLEditExpressionSuccessPayload extends GQLEditExpressionPayload { + newExpressionId: string; +} diff --git a/frontend/syson-components/src/extensions/expressions/useExpressionTextualRepresentation.ts b/frontend/syson-components/src/extensions/expressions/useExpressionTextualRepresentation.ts new file mode 100644 index 000000000..6c3884b82 --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/useExpressionTextualRepresentation.ts @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +import { gql, useQuery } from '@apollo/client'; +import { useMultiToast } from '@eclipse-sirius/sirius-components-core'; +import { useEffect } from 'react'; +import { + GQLGetExpressionTextualRepresentationData, + GQLGetExpressionTextualRepresentationVariables, + UseExpressionTextualRepresentationValue, +} from './useExpressionTextualRepresentation.types'; + +const getExpressionTextualRepresentationQuery = gql` + query getExpressionTextualRepresentation($editingContextId: ID!, $elementId: ID!) { + viewer { + editingContext(editingContextId: $editingContextId) { + expressionTextualRepresentation(elementId: $elementId) + } + } + } +`; + +export const useExpressionTextualRepresentation = ( + editingContextId: string, + elementId: string | null +): UseExpressionTextualRepresentationValue => { + const { loading, data, error } = useQuery< + GQLGetExpressionTextualRepresentationData, + GQLGetExpressionTextualRepresentationVariables + >(getExpressionTextualRepresentationQuery, { + variables: { editingContextId, elementId: elementId || '' }, + skip: elementId === null, + }); + + const { addErrorMessage } = useMultiToast(); + + useEffect(() => { + if (error) { + addErrorMessage(error.message); + } + }, [error]); + + const textualRepresentation: string | null = data?.viewer.editingContext.expressionTextualRepresentation ?? null; + + return { textualRepresentation, loading }; +}; diff --git a/frontend/syson-components/src/extensions/expressions/useExpressionTextualRepresentation.types.ts b/frontend/syson-components/src/extensions/expressions/useExpressionTextualRepresentation.types.ts new file mode 100644 index 000000000..988871091 --- /dev/null +++ b/frontend/syson-components/src/extensions/expressions/useExpressionTextualRepresentation.types.ts @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2026 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +export interface UseExpressionTextualRepresentationValue { + textualRepresentation: string | null; + loading: boolean; +} + +export interface GQLGetExpressionTextualRepresentationVariables { + editingContextId: string; + elementId: string; +} + +export interface GQLGetExpressionTextualRepresentationData { + viewer: GQLGetExpressionTextualRepresentationViewer; +} + +export interface GQLGetExpressionTextualRepresentationViewer { + editingContext: GQLGetExpressionTextualRepresentationEditingContext; +} + +export interface GQLGetExpressionTextualRepresentationEditingContext { + expressionTextualRepresentation: string | null; +} diff --git a/frontend/syson-components/src/extensions/registry/SysONExtensionRegistry.ts b/frontend/syson-components/src/extensions/registry/SysONExtensionRegistry.ts index d2cbde831..9f6e48a70 100644 --- a/frontend/syson-components/src/extensions/registry/SysONExtensionRegistry.ts +++ b/frontend/syson-components/src/extensions/registry/SysONExtensionRegistry.ts @@ -37,13 +37,15 @@ import { ImportLibraryCommand, navigationBarMenuIconExtensionPoint, } from '@eclipse-sirius/sirius-web-application'; - import { Edge, Node, useStoreApi } from '@xyflow/react'; import { SysMLImportedPackageNodePaletteAppearanceSection } from '../../nodes/imported_package/SysMLImportedPackageNodePaletteAppearanceSection'; import { SysMLNoteNodePaletteAppearanceSection } from '../../nodes/note/SysMLNoteNodePaletteAppearanceSection'; import { SysMLPackageNodePaletteAppearanceSection } from '../../nodes/package/SysMLPackageNodePaletteAppearanceSection'; import { sysMLNodesStyleDocumentTransform } from '../../nodes/SysMLNodesDocumentTransform'; import { SysMLViewFrameNodePaletteAppearanceSection } from '../../nodes/view_frame/SysMLViewFrameNodePaletteAppearanceSection'; +import { DeleteSysMLExpressionMenuContribution } from '../expressions/DeleteSysMLExpressionMenuContribution'; +import { EditSysMLExpressionMenuContribution } from '../expressions/EditSysMLExpressionMenuContribution'; +import { NewSysMLExpressionMenuContribution } from '../expressions/NewSysMLExpressionMenuContribution'; import { InsertTextualSysMLMenuContribution } from '../InsertTextualSysMLv2MenuContribution'; import { SysONNavigationBarMenuIcon } from '../navigationBarMenu/SysONNavigationBarMenuIcon'; import { PublishProjectSysMLContentsAsLibraryCommand } from '../omnibox/PublishProjectSysMLContentsAsLibraryCommand'; @@ -104,6 +106,24 @@ const treeItemContextMenuOverrideContributions: TreeItemContextMenuOverrideContr }, component: InsertTextualSysMLMenuContribution, }, + { + canHandle: (entry: GQLTreeItemContextMenuEntry) => { + return entry.id === 'createExpression'; + }, + component: NewSysMLExpressionMenuContribution, + }, + { + canHandle: (entry: GQLTreeItemContextMenuEntry) => { + return entry.id === 'editExpression'; + }, + component: EditSysMLExpressionMenuContribution, + }, + { + canHandle: (entry: GQLTreeItemContextMenuEntry) => { + return entry.id === 'deleteExpression'; + }, + component: DeleteSysMLExpressionMenuContribution, + }, ]; sysONExtensionRegistry.putData( diff --git a/scripts/check-coverage.jsh b/scripts/check-coverage.jsh index 6a5793be5..210cf7bc2 100755 --- a/scripts/check-coverage.jsh +++ b/scripts/check-coverage.jsh @@ -29,13 +29,13 @@ double checkCoverage(String module) { } record ModuleCoverage(String moduleName, double expectedCoverage) {} -double expectedGlobalCoverage = 69.0; +double expectedGlobalCoverage = 70.0; var moduleCoverageData = List.of( new ModuleCoverage("syson-application", 37.0), - new ModuleCoverage("syson-application-configuration", 75.0), + new ModuleCoverage("syson-application-configuration", 76.0), new ModuleCoverage("syson-common-view", 100.0), new ModuleCoverage("syson-diagram-common-view", 89.0), - new ModuleCoverage("syson-diagram-services", 85.0), + new ModuleCoverage("syson-diagram-services", 86.0), new ModuleCoverage("syson-direct-edit-grammar", 67.0), new ModuleCoverage("syson-form-services", 100.0), new ModuleCoverage("syson-model-services", 94.0), @@ -47,14 +47,14 @@ var moduleCoverageData = List.of( new ModuleCoverage("syson-sysml-export", 71.0), new ModuleCoverage("syson-sysml-import", 85.0), new ModuleCoverage("syson-sysml-metamodel", 77.0), - new ModuleCoverage("syson-sysml-metamodel-edit", 17.0), + new ModuleCoverage("syson-sysml-metamodel-edit", 18.0), new ModuleCoverage("syson-sysml-metamodel-services", 93.0), new ModuleCoverage("syson-sysml-rest-api-services", 93.0), new ModuleCoverage("syson-sysml-validation", 97.0), new ModuleCoverage("syson-table-requirements-view", 73.0), new ModuleCoverage("syson-table-services", 100.0), new ModuleCoverage("syson-tree-explorer-view", 87.0), - new ModuleCoverage("syson-tree-services", 80.0) + new ModuleCoverage("syson-tree-services", 81.0) ); void display(String module, double coverage, double expected) {