From 4c4c7a46ff05f655c869cf0de1519b0b4d0a1699 Mon Sep 17 00:00:00 2001 From: SaintierFr <99645240+SaintierFr@users.noreply.github.com> Date: Thu, 16 Feb 2023 16:46:31 +0100 Subject: [PATCH] Merge develop into main (#242) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(216): check that Datasets, FCDAs, ControlBlocks (GOOSE, REPORT and SMV) satisfy limits fixed by config Signed-off-by: Aliou DIAITE * build(deps): bump alex-page/github-project-automation-plus Bumps [alex-page/github-project-automation-plus](https://github.com/alex-page/github-project-automation-plus) from 0.8.2 to 0.8.3. - [Release notes](https://github.com/alex-page/github-project-automation-plus/releases) - [Commits](https://github.com/alex-page/github-project-automation-plus/compare/v0.8.2...v0.8.3) --- updated-dependencies: - dependency-name: alex-page/github-project-automation-plus dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * feat(216): check that Datasets, FCDAs, ControlBlocks (GOOSE, REPORT and SMV) satisfy limits fixed by config Signed-off-by: Aliou DIAITE * chore(217): use built-in Maven cache mechanism of setup-java GitHub Action fixes #217 Signed-off-by: Aurélien Pupier * Fix typos in docs Signed-off-by: Aurélien Pupier * feat/javadoc: automate Javadoc deployment via gh-pages (#229) fixes maven plugin related to jdk17 Signed-off-by: Samir Romdhani feat/javadoc: automate Javadoc remove PR creation and push directly gh-pages branch * feat(231): Automatic binding: Edit InRef from ExtRef Signed-off-by: SaintierFr <99645240+SaintierFr@users.noreply.github.com> * fix(#235): Error occurs when importing STD file during SCD automated creation Signed-off-by: SaintierFr <99645240+SaintierFr@users.noreply.github.com> * fix(#239): Exclude DOI Mod-stVal in DAI updatability check (#240) * fix(#239): Exclude DOI Mod-stVal in DAI updatability check * fix(#239): Comment unit test in error while bug #241 is not fixed Signed-off-by: SaintierFr <99645240+SaintierFr@users.noreply.github.com> --------- Signed-off-by: Aliou DIAITE Signed-off-by: dependabot[bot] Signed-off-by: Aurélien Pupier Signed-off-by: SaintierFr <99645240+SaintierFr@users.noreply.github.com> Signed-off-by: SaintierFr <99645240+SaintierFr@users.noreply.github.com> Co-authored-by: Aliou DIAITE Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Pascal Wilbrink Co-authored-by: Aurélien Pupier Co-authored-by: Samir Romdhani --- .github/workflows/automate_javadoc.yml | 50 +- .github/workflows/automate_projects.yml | 8 +- .github/workflows/sonarcloud-analysis.yml | 10 +- docs/HOME.md | 20 +- docs/compas-sct.md | 2 +- pom.xml | 1 - .../lfenergy/compas/sct/app/package-info.java | 2 +- .../compas/sct/commons/dto/ExtrefTarget.java | 9 + .../compas/sct/commons/dto/SclReport.java | 1 - .../compas/sct/commons/dto/package-info.java | 4 +- .../compas/sct/commons/scl/ExtRefService.java | 1 + .../sct/commons/scl/ObjectReference.java | 29 +- .../sct/commons/scl/PrivateService.java | 13 +- .../compas/sct/commons/scl/SclService.java | 51 +- .../sct/commons/scl/com/package-info.java | 4 +- .../sct/commons/scl/dtt/package-info.java | 4 +- .../sct/commons/scl/header/package-info.java | 4 +- .../commons/scl/ied/AbstractDAIAdapter.java | 94 ++-- .../commons/scl/ied/AbstractLNAdapter.java | 84 ++-- .../commons/scl/ied/AccessPointAdapter.java | 340 +++++++++++++ .../sct/commons/scl/ied/DOIAdapter.java | 125 ++++- .../sct/commons/scl/ied/IEDAdapter.java | 51 +- .../sct/commons/scl/ied/InputsAdapter.java | 1 + .../sct/commons/scl/ied/LN0Adapter.java | 64 ++- .../sct/commons/scl/ied/package-info.java | 4 +- .../compas/sct/commons/scl/package-info.java | 4 +- .../commons/scl/sstation/package-info.java | 4 +- .../sct/commons/util/ServicesConfigEnum.java | 14 + .../sct/commons/scl/PrivateServiceTest.java | 4 +- .../sct/commons/scl/SclServiceTest.java | 209 +++++++- .../scl/ied/AccessPointAdapterTest.java | 278 +++++++++++ .../sct/commons/scl/ied/DOIAdapterTest.java | 446 +++++++++++++++--- .../sct/commons/scl/ied/IEDAdapterTest.java | 88 +++- .../commons/scl/ied/InputsAdapterTest.java | 28 ++ .../sct/commons/scl/ied/LN0AdapterTest.java | 422 +++++++++++++---- .../sct/commons/scl/ied/LNAdapterTest.java | 133 +++++- .../sct/commons/testhelpers/SclHelper.java | 3 + .../scd_check_coherent_extRefs.xml | 44 ++ ...ck_limitation_binded_ied_controls_fcda.xml | 148 ++++++ ..._check_limitation_ied_controls_dataset.xml | 89 ++++ ...aset_and_controlblocks_success_analyze.xml | 130 +++++ ...mpas_icd_header_in_different_functions.xml | 33 ++ ...ssue_165_enhance_68_Test_Dai_Updatable.scd | 74 +++ .../scd_update_inref_issue_231_test_ko.xml | 115 +++++ .../scd_update_inref_issue_231_test_ok.xml | 86 ++++ .../scd_update_inref_test.xml | 177 +++++++ 46 files changed, 3102 insertions(+), 403 deletions(-) create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ExtrefTarget.java create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java create mode 100644 sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java create mode 100644 sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java create mode 100644 sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml create mode 100644 sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml create mode 100644 sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml create mode 100644 sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml create mode 100644 sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml create mode 100644 sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml create mode 100644 sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml create mode 100644 sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml diff --git a/.github/workflows/automate_javadoc.yml b/.github/workflows/automate_javadoc.yml index 3ac03a52c..2db3fdbca 100644 --- a/.github/workflows/automate_javadoc.yml +++ b/.github/workflows/automate_javadoc.yml @@ -1,27 +1,29 @@ -# SPDX-FileCopyrightText: 2022 RTE FRANCE +# SPDX-FileCopyrightText: 2022 2023 RTE FRANCE # # SPDX-License-Identifier: Apache-2.0 name: Continuous Deployment - JavaDocs on: - push: - branches: - - 'feat/javadoc' + pull_request_target: + types: [ labeled, closed ] + jobs: build: + if: ${{ github.event.pull_request.base.ref == 'develop' && github.actor != 'dependabot[bot]' && github.event.pull_request.merged && contains(github.event.pull_request.labels.*.name, 'javadoc') }} name: Java Docs runs-on: ubuntu-latest env: - from_branch: feat/javadoc - target_branch: feat/117_Documentation_SCD_Generation_Process + DEPLOYMENT_BRANCH: gh-pages + DEPENDABOT_BRANCH: dependabot/javadoc-${{ github.head_ref || github.ref_name }}-${{ github.event.pull_request.number }} steps: - # Use develop as base branch to generate javadoc - - name: Setup Environment + - name: Checkout uses: actions/checkout@v3 + + - name: Set up JDK 17 + uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '17' - ref: ${{ env.target_branch }} - name: Create custom Maven Settings.xml uses: whelk-io/maven-settings-xml-action@v21 @@ -33,6 +35,8 @@ jobs: run: | git config --global user.name '${{ secrets.CONFIG_CI_USER_NAME }}' git config --global user.email '${{ secrets.CONFIG_CI_USER_EMAIL }}' + git config pull.rebase false + git fetch origin ${{ env.DEPLOYMENT_BRANCH }} - name: Import GPG key id: import_gpg @@ -42,27 +46,25 @@ jobs: git_user_signingkey: true git_commit_gpgsign: true - - name: Prepare Pull Request branch + # Take note that your GitHub Pages site is currently being built from the /docs folder in the DEPLOYMENT_BRANCH branch. + - name: Prepare Pull Request branch & commit files run: | mvn -s custom_maven_settings.xml clean javadoc:aggregate -P javadoc mkdir -p docs/javadoc yes | cp -Rf target/site/apidocs/* docs/javadoc/ - git checkout -b ${{ env.from_branch }}-pull-request + git checkout -b temp + git add docs/javadoc/ + git commit -m "[dependabot/javadoc/temp]: update javadoc" + git checkout ${{ env.DEPLOYMENT_BRANCH }} + git clean -fdx + git checkout -b ${{ env.DEPENDABOT_BRANCH }} + git cherry-pick -n -X theirs temp + git commit -m "[dependabot/javadoc]: update javadoc" + git checkout ${{ env.DEPLOYMENT_BRANCH }} + git merge ${{ env.DEPENDABOT_BRANCH }} - # Note that will fail if branch already exists. - name: Push Git Branch uses: ad-m/github-push-action@v0.6.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} - branch: ${{ env.from_branch }}-pull-request - - # Note that will silently fail if PR already exists. - - name: Create Pull Request - uses: repo-sync/pull-request@v2 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - source_branch: ${{ env.from_branch }}-pull-request - destination_branch: ${{ env.target_branch }} - pr_title: "Docs: Update Java docs repository" - pr_body: "Automatically created from CI workflow" - pr_label: "documentation,javadoc" \ No newline at end of file + branch: ${{ env.DEPLOYMENT_BRANCH }} \ No newline at end of file diff --git a/.github/workflows/automate_projects.yml b/.github/workflows/automate_projects.yml index 40f2e7621..c7e2661d4 100644 --- a/.github/workflows/automate_projects.yml +++ b/.github/workflows/automate_projects.yml @@ -14,14 +14,14 @@ jobs: steps: - name: add-new-issues-to-repository-based-project-column if: github.event_name == 'issues' && github.event.action == 'opened' - uses: alex-page/github-project-automation-plus@v0.8.2 + uses: alex-page/github-project-automation-plus@v0.8.3 with: project: CoMPAS SCT Board column: To do repo-token: ${{ secrets.ORG_GITHUB_ACTION_SECRET }} - name: add-new-pull-request-to-repository-based-project-column if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && github.event.action == 'opened' - uses: alex-page/github-project-automation-plus@v0.8.2 + uses: alex-page/github-project-automation-plus@v0.8.3 with: project: CoMPAS SCT Board column: To do @@ -29,14 +29,14 @@ jobs: - name: add-new-issues-to-organization-based-project-column if: github.event_name == 'issues' && github.event.action == 'opened' - uses: alex-page/github-project-automation-plus@v0.8.2 + uses: alex-page/github-project-automation-plus@v0.8.3 with: project: CoMPAS Issues Overview Board column: To do repo-token: ${{ secrets.ORG_GITHUB_ACTION_SECRET }} - name: add-new-pull-request-to-organization-based-project-column if: (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && github.event.action == 'opened' - uses: alex-page/github-project-automation-plus@v0.8.2 + uses: alex-page/github-project-automation-plus@v0.8.3 with: project: CoMPAS Pull Request Overview Board column: To do diff --git a/.github/workflows/sonarcloud-analysis.yml b/.github/workflows/sonarcloud-analysis.yml index 8ad3c7ef2..09cd08540 100644 --- a/.github/workflows/sonarcloud-analysis.yml +++ b/.github/workflows/sonarcloud-analysis.yml @@ -26,24 +26,18 @@ jobs: key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - - name: Cache Maven packages - uses: actions/cache@v3 - with: - path: ~/.m2 - key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} - restore-keys: ${{ runner.os }}-m2 - - name: Set up JDK 17 uses: actions/setup-java@v3 with: distribution: 'zulu' java-version: '17' + cache: 'maven' - name: Set Common Sonar Variables id: sonar_env run: | echo "##[set-output name=sonar_opts;]$(echo -Dsonar.host.url=https://sonarcloud.io \ - -Dsonar.projectKey=com-pas_compas-core \ + -Dsonar.projectKey=com-pas_compas-sct \ -Dsonar.organization=com-pas )" - name: Create custom Maven Settings.xml uses: whelk-io/maven-settings-xml-action@v21 diff --git a/docs/HOME.md b/docs/HOME.md index fc77382dc..1cea5e2cc 100644 --- a/docs/HOME.md +++ b/docs/HOME.md @@ -39,7 +39,7 @@ Technical Steering Committee [CoMPAS-tsc@lists.lfenergy.org](mailto:CoMPAS-tsc@l By contributing to the CoMPAS project, you accept and agree to the following terms and conditions for your present and future contributions submitted to CoMPAS. All contributions to this project are licensed under the license stipulated at the corresponding sub-repository. -Except where otherwise explicitely indicated, CoMPAS is an open source project licensed under the [Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0/). +Except where otherwise explicitly indicated, CoMPAS is an open source project licensed under the [Apache License, version 2.0](http://www.apache.org/licenses/LICENSE-2.0/). The project requires the use of the [Developer Certificate of Origin (DCO)](https://developercertificate.org/). The DCO is a legally binding statement asserting that you are you have the right to submit your contribution and to license it under the project's applicable license. @@ -64,21 +64,21 @@ Note that checks will be performed during the integration in order to require th ## Community refinements -Every monday there is a CoMPAS Community Refinement at 10AM CET. The event is available at the CoMPAS mailing list [calendar](https://lists.lfenergy.org/g/CoMPAS/calendar) and can be subscribed to. +Every Monday there is a CoMPAS Community Refinement at 10AM CET. The event is available at the CoMPAS mailing list [calendar](https://lists.lfenergy.org/g/CoMPAS/calendar) and can be subscribed to. ### Prepared topics -Every friday before the refinement meeting on Monday, all topics are published on the #compas channel on our [LFEnergy Slack](https://lfenergy.slack.com/). +Every Friday before the refinement meeting on Monday, all topics are published on the #compas channel on our [LFEnergy Slack](https://lfenergy.slack.com/). ### Add your own topics -Everybody can suggest topics for the refinement. To do this, join the [LFEnergy Slack](https://lfenergy.slack.com/) if not already, and put your topics in the #compas channel. You can just do this by writing a message like "I want to add something to the refinement this monday, namely..." or add a comment to the already prepared topics if available (see [Prepared topics](#prepared-topics)). +Everybody can suggest topics for the refinement. To do this, join the [LFEnergy Slack](https://lfenergy.slack.com/) if not already, and put your topics in the #compas channel. You can just do this by writing a message like "I want to add something to the refinement this Monday, namely..." or add a comment to the already prepared topics if available (see [Prepared topics](#prepared-topics)). ### Reporting Bugs and Suggesting Enhancements Bugs and enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue in the repository where it belongs (an issue about the CIM to IEC 61850 mapping belongs in the [CIM Mapping repository](https://github.com/com-pas/compas-cim-mapping) for example) and provide the following information by filling in the template, which is either an issue or a bug. -When an issue is created, it is automatically being added to the `To do` column of the specific repository. This means it's added, but not yet refined. Every monday at 10AM CET, there is a Community Refinement (see our mailing list [calendar](https://lists.lfenergy.org/g/CoMPAS/calendar), everybody can join) where issues are being discussed that are not refined yet. Your issue is one of them. +When an issue is created, it is automatically being added to the `To do` column of the specific repository. This means it's added, but not yet refined. Every Monday at 10AM CET, there is a Community Refinement (see our mailing list [calendar](https://lists.lfenergy.org/g/CoMPAS/calendar), everybody can join) where issues are being discussed that are not refined yet. Your issue is one of them. Once it's accepted and refined, it goes to the `Ready for development` column and it's ready to be implemented/fixed. Before creating bug reports or suggesting enhancement, please **perform a [cursory search](https://github.com/search?q=+is%3Aissue+user%3Acom-pas)** @@ -118,8 +118,8 @@ Continuous integration is set up to run on all branches automatically and will o ### Tools to contribute Continuous integration is setup automatically on all contributions. However, it's faster to iterate locally to fix problems than waiting for the status checks to finish. -There are many tools that can be used to do the verifications that are enforced by all status checks. The most simple and universal tool is maven, but IDE integrations -can be used to get more immediate feedback. Most of the team uses IntelliJ IDEA, but others IDEs can be used, for exemple the Eclipse IDE. +There are many tools that can be used to do the verifications that are enforced by all status checks. The most simple and universal tool is Maven, but IDE integrations +can be used to get more immediate feedback. Most of the team uses IntelliJ IDEA, but others IDEs can be used, for example the Eclipse IDE. ### Definition of Done @@ -147,7 +147,7 @@ On the [developing](https://com-pas.github.io/contributing/DEVELOPING.html) page #### Open Community Calls -It's good to know that every other monday, we are having a so called Open Community Call. Everyone participating in the CoMPAS project can join +It's good to know that every other Monday, we are having a so called Open Community Call. Everyone participating in the CoMPAS project can join and talk about and ask question about the CoMPAS project. When the Open Community Calls are taking place, can be found at the [General CoMPAS mailing list calendar](https://lists.lfenergy.org/g/CoMPAS/calendar). @@ -167,7 +167,7 @@ A good (open source) project requires documentation. We have two places for our ##### LF Energy Wiki -LF Energy has it's own [CoMPAS specific Wiki](https://wiki.lfenergy.org/display/HOME/CoMPAS). This is the place for documenation +LF Energy has it's own [CoMPAS specific Wiki](https://wiki.lfenergy.org/display/HOME/CoMPAS). This is the place for documentation about CoMPAS in general (like roadmap and the community call agendas). #### Architecture and technologies @@ -178,7 +178,7 @@ please check the source code (duh!) and our [CoMPAS Architecture Github Pages](h ### Copyright and Licensing Copyright and license information is done on per-file basis. We use the specification of [REUSE](https://reuse.software/spec/) -to ensure that copyright information of the project is clear and can be analuzed in an automated fashion. +to ensure that copyright information of the project is clear and can be analyzed in an automated fashion. Every source code repository within CoMPAS has a Github Action for checking against the REUSE specification. diff --git a/docs/compas-sct.md b/docs/compas-sct.md index 4e231e2ef..d6b979443 100644 --- a/docs/compas-sct.md +++ b/docs/compas-sct.md @@ -170,7 +170,7 @@ import it in the temporary SCD file ``` Operations can now be realized and validated on this minimal file (which has the same structure as an ICD file). -Any validation on the minimal file guaranties a valid integration of those chunks into the large file. +Any validation on the minimal file guarantees a valid integration of those chunks into the large file. > **WARNING:** This concern only update/creation operations on SCL file. diff --git a/pom.xml b/pom.xml index a8de927d6..caaaded0e 100644 --- a/pom.xml +++ b/pom.xml @@ -218,7 +218,6 @@ ${javadoc.title} ${javadoc.lint} - --no-module-directories diff --git a/sct-app/src/main/java/org/lfenergy/compas/sct/app/package-info.java b/sct-app/src/main/java/org/lfenergy/compas/sct/app/package-info.java index c83b8e50c..fe9a2d02f 100644 --- a/sct-app/src/main/java/org/lfenergy/compas/sct/app/package-info.java +++ b/sct-app/src/main/java/org/lfenergy/compas/sct/app/package-info.java @@ -3,6 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

sct-app is a group of automation services

+ *

sct-app is a group of automation services

*/ package org.lfenergy.compas.sct.app; \ No newline at end of file diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ExtrefTarget.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ExtrefTarget.java new file mode 100644 index 000000000..d1eb10e5d --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/ExtrefTarget.java @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: 2023 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.dto; + +public enum ExtrefTarget { + SRC_REF, SRC_CB +} diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/SclReport.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/SclReport.java index 05103dc8d..12e11624a 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/SclReport.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/SclReport.java @@ -39,5 +39,4 @@ public class SclReport { public boolean isSuccess() { return sclReportItems.stream().noneMatch(SclReportItem::isFatal); } - } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/package-info.java index ee24e919f..9f762c71b 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/dto/package-info.java @@ -3,8 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

sct.commons.dto is a group of DTO utils for operating on + *

sct.commons.dto is a group of DTO utils for operating on * {@link org.lfenergy.compas.scl2007b4.model.SCL SCL} services - *

+ * */ package org.lfenergy.compas.sct.commons.dto; \ No newline at end of file diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java index 57f516127..6e6f0f9f2 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ExtRefService.java @@ -26,6 +26,7 @@ public class ExtRefService { private static final String MESSAGE_MISSING_IED_NAME_PARAMETER = "IED.name parameter is missing"; + private static final String CLIENT_IED_NAME = "The Client IED "; /** * Updates iedName attribute of all ExtRefs in the Scd. diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ObjectReference.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ObjectReference.java index e37e9cbe4..0701ea0e4 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ObjectReference.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ObjectReference.java @@ -6,7 +6,10 @@ import lombok.Getter; import org.apache.commons.lang3.StringUtils; - +import org.lfenergy.compas.scl2007b4.model.TExtRef; +import org.lfenergy.compas.scl2007b4.model.TLLN0Enum; +import org.lfenergy.compas.sct.commons.dto.ExtrefTarget; +import org.lfenergy.compas.sct.commons.util.Utils; /** * A representation of the model object @@ -38,7 +41,7 @@ public class ObjectReference { private static final String MALFORMED_OBJ_REF = "Malformed ObjRef : %s" ; private final String reference; - //IEDName.LDInst + //IEDNameLDInst private String ldName; //LNPrefixLNClassLNInst private String lNodeName; @@ -49,14 +52,30 @@ public ObjectReference(String reference) { init(); } + public ObjectReference(TExtRef extRef, ExtrefTarget target) { + this.ldName = extRef.getIedName() + extRef.getLdInst(); + if(target.equals(ExtrefTarget.SRC_REF)) { + this.lNodeName = Utils.emptyIfBlank(extRef.getPrefix()) + + (extRef.isSetLnClass() ? extRef.getLnClass().get(0): TLLN0Enum.LLN_0.value()) + + Utils.emptyIfBlank(extRef.getLnInst()); + this.dataAttributes = Utils.emptyIfBlank(extRef.getDoName()); + } + if(target.equals(ExtrefTarget.SRC_CB)) { + this.lNodeName = Utils.emptyIfBlank(extRef.getSrcPrefix()) + + (extRef.isSetSrcLNClass() ? extRef.getSrcLNClass().get(0): TLLN0Enum.LLN_0.value()) + + Utils.emptyIfBlank(extRef.getSrcLNInst()); + this.dataAttributes = Utils.emptyIfBlank(extRef.getSrcCBName()); + } + this.reference = this.ldName + "/" + this.lNodeName + "." + this.dataAttributes; + } + public final void init(){ - String refCopy = reference; - String[] objRefPart = reference.split("[/]"); + String[] objRefPart = reference.split("/"); if(objRefPart.length != 2 || StringUtils.isBlank(objRefPart[0]) || StringUtils.isBlank(objRefPart[1])){ throw new IllegalArgumentException(String.format(MALFORMED_OBJ_REF, reference)); } ldName = objRefPart[0]; - refCopy = objRefPart[1]; + String refCopy = objRefPart[1]; objRefPart = refCopy.split("[.]", 2); if(objRefPart.length != 2 || StringUtils.isBlank(objRefPart[0]) || StringUtils.isBlank(objRefPart[1])){ throw new IllegalArgumentException(String.format(MALFORMED_OBJ_REF, reference)); diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/PrivateService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/PrivateService.java index ec65ce786..60602ea3d 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/PrivateService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/PrivateService.java @@ -12,6 +12,7 @@ import javax.xml.bind.JAXBElement; import java.util.*; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import static org.lfenergy.compas.sct.commons.util.CommonConstants.*; @@ -298,12 +299,12 @@ public static String stdCheckFormatExceptionMessage(TPrivate key) throws ScdExce /** - * Creates map of IEDName and related Private for all Privates COMPAS-ICDHeader in /Substation of SCL + * Creates stream of Private for all Privates COMPAS-ICDHeader in /Substation of SCL * * @param scdRootAdapter SCL file in which Private should be found - * @return map of Private and its IEDName parameter + * @return stream of COMPAS-ICDHeader Private */ - public static Stream createMapIEDNameAndPrivate(SclRootAdapter scdRootAdapter) { + public static Stream streamIcdHeaderPrivatesWithDistinctIEDName(SclRootAdapter scdRootAdapter) { return scdRootAdapter.getCurrentElem().getSubstation().get(0).getVoltageLevel().stream() .map(TVoltageLevel::getBay).flatMap(Collection::stream) .map(TBay::getFunction).flatMap(Collection::stream) @@ -311,8 +312,10 @@ public static Stream createMapIEDNameAndPrivate(SclRootAdapter scdRoot .map(TLNode::getPrivate).flatMap(Collection::stream) .filter(tPrivate -> tPrivate.getType().equals(COMPAS_ICDHEADER.getPrivateType()) - && PrivateService.extractCompasICDHeader(tPrivate).isPresent() - && PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName() != null); + && PrivateService.extractCompasICDHeader(tPrivate).map(TCompasICDHeader::getIEDName).isPresent()) + .collect(Collectors.groupingBy(tPrivate -> PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName())) + .values().stream() + .map(tPrivates -> tPrivates.get(0)); } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java index fa177e8d5..cca4964ad 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/SclService.java @@ -234,10 +234,11 @@ public static List getExtRefInfo(SCL scd, String iedName, String ldI /** * Create LDevice - * @param scd - * @param iedName - * @param ldInst - * @return + * + * @param scd SCL file in which LDevice should be found + * @param iedName name of IED in which LDevice is localized + * @param ldInst LdInst of LDevice for which adapter is created + * @return created LDevice adapter */ private static LDeviceAdapter createLDeviceAdapter(SCL scd, String iedName, String ldInst) { SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); @@ -501,11 +502,11 @@ public static SclRootAdapter importSTDElementsInSCD(@NonNull SclRootAdapter scdR PrivateService.checkSTDCorrespondanceWithLNodeCompasICDHeader(mapICDSystemVersionUuidAndSTDFile); // List all Private and remove duplicated one with same iedName //For each Private.ICDSystemVersionUUID and Private.iedName find STD File - PrivateService.createMapIEDNameAndPrivate(scdRootAdapter).forEach(tPrivate -> { + PrivateService.streamIcdHeaderPrivatesWithDistinctIEDName(scdRootAdapter).forEach(tPrivate -> { String iedName = PrivateService.extractCompasICDHeader(tPrivate).get().getIEDName(); String icdSysVerUuid = PrivateService.extractCompasICDHeader(tPrivate).map(TCompasICDHeader::getICDSystemVersionUUID) - .orElseThrow(() -> new ScdException(ICD_SYSTEM_VERSION_UUID + " is not present in COMPAS-ICDHeader in LNode") - ); + .orElseThrow(() -> new ScdException(ICD_SYSTEM_VERSION_UUID + " is not present in COMPAS-ICDHeader in LNode") + ); if (!mapICDSystemVersionUuidAndSTDFile.containsKey(icdSysVerUuid)) throw new ScdException("There is no STD file found corresponding to " + PrivateService.stdCheckFormatExceptionMessage(tPrivate)); // import /ied /dtt in Scd @@ -572,4 +573,40 @@ public static SclReport updateLDeviceStatus(SCL scd) { sclReport.setSclRootAdapter(sclRootAdapter); return sclReport; } + + /** + * Checks Control Blocks, DataSets and FCDA number limitation into Access Points + * + * @param scd SCL file for which LDevice should be activated or deactivated + * @return SclReport Object that contain SCL file and set of errors + */ + public static SclReport analyzeDataGroups(SCL scd) { + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + List sclReportItems = sclRootAdapter.streamIEDAdapters() + .map(iedAdapter -> { + List list = new ArrayList<>(); + list.addAll(iedAdapter.checkDataGroupCoherence()); + list.addAll(iedAdapter.checkBindingDataGroupCoherence()); + return list; + }).flatMap(Collection::stream).toList(); + + return new SclReport(sclRootAdapter, sclReportItems); + } + + /** + * Update DAIs of DO InRef in all LN0 of the SCD using matching ExtRef information. + * + * @param scd SCL file for which DOs InRef should be updated with matching ExtRef information + * @return SclReport Object that contain SCL file and set of errors + */ + public static SclReport updateDoInRef(SCL scd) { + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + List sclReportItems = sclRootAdapter.streamIEDAdapters() + .flatMap(IEDAdapter::streamLDeviceAdapters) + .map(LDeviceAdapter::getLN0Adapter) + .map(LN0Adapter::updateDoInRef) + .flatMap(List::stream) + .toList(); + return new SclReport(sclRootAdapter, sclReportItems); + } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/package-info.java index fbf637d8e..8b72f2343 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/com/package-info.java @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

scl.com is a group of services for operating on + *

scl.com is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.TCommunication Communication} object - *

+ * * @see org.lfenergy.compas.scl2007b4.model.TCommunication * @see org.lfenergy.compas.scl2007b4.model.TSubNetwork * @see org.lfenergy.compas.scl2007b4.model.TConnectedAP diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/package-info.java index 81afa71b1..020d29d34 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/dtt/package-info.java @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

scl.dtt is a group of services for operating on + *

scl.dtt is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.TDataTypeTemplates DataTypeTemplates} object - *

+ * * @see org.lfenergy.compas.scl2007b4.model.TLNodeType * @see org.lfenergy.compas.scl2007b4.model.TDOType * @see org.lfenergy.compas.scl2007b4.model.TDAType diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/header/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/header/package-info.java index 55eaf1e46..53782746e 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/header/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/header/package-info.java @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

scl.header is a group of services for operating on + *

scl.header is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.THeader Header} object - *

+ * * @see org.lfenergy.compas.scl2007b4.model.THeader * @see org.lfenergy.compas.scl2007b4.model.THitem * @see Issue !6 diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java index d612a8cee..3d6e69e45 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractDAIAdapter.java @@ -5,14 +5,14 @@ package org.lfenergy.compas.sct.commons.scl.ied; import org.lfenergy.compas.scl2007b4.model.TDAI; +import org.lfenergy.compas.scl2007b4.model.TDOI; import org.lfenergy.compas.scl2007b4.model.TPrivate; import org.lfenergy.compas.scl2007b4.model.TVal; import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; +import org.lfenergy.compas.sct.commons.util.CommonConstants; import java.util.Map; -import java.util.Optional; -import java.util.stream.Stream; /** * A representation of the model object @@ -35,20 +35,18 @@ *
  • {@link AbstractDAIAdapter#addPrivate(TPrivate) Add TPrivate under this object}
  • * *
  • Checklist functions
  • - *
      - *
    • {@link AbstractDAIAdapter#isValImport Check value Of valImport attribute}
    • - *
    * * * @see org.lfenergy.compas.scl2007b4.model.TDAI * @see Issue !70 */ -public abstract class AbstractDAIAdapter

    extends SclElementAdapter implements IDataAdapter{ +public abstract class AbstractDAIAdapter

    extends SclElementAdapter implements IDataAdapter { /** * Constructor + * * @param parentAdapter Parent container reference - * @param currentElem Current reference + * @param currentElem Current reference */ protected AbstractDAIAdapter(P parentAdapter, TDAI currentElem) { super(parentAdapter, currentElem); @@ -56,9 +54,10 @@ protected AbstractDAIAdapter(P parentAdapter, TDAI currentElem) { /** * Gets SDI from DAI by name + * * @param sName SDI name + * @param expected class type * @return IDataAdapter related object - * @param expected class type * @throws ScdException throws when specified SDI not present in DAI */ public S getStructuredDataAdapterByName(String sName) throws ScdException { @@ -67,33 +66,34 @@ public S getStructuredDataAdapterByName(String sName) t /** * Gets DataAdapter by DAI + * * @param sName DAI name + * @param expected class type * @return IDataAdapter related object - * @param expected class type * @throws ScdException throws when specified DAI unknown */ - public S getDataAdapterByName(String sName) throws ScdException { + public S getDataAdapterByName(String sName) throws ScdException { throw new UnsupportedOperationException("DAI doesn't have any DAI"); } /** * Sets ValImport value + * * @param b value */ - public void setValImport(boolean b){ + public void setValImport(boolean b) { currentElem.setValImport(b); } - /** - * Cheks ValImport boolean value - * @return Boolean value of ValImport if define or null - */ - public Boolean isValImport(){ - return currentElem.isSetValImport() ? currentElem.isValImport() : null; + private boolean isDOModDAstVal() { + if (parentAdapter.getCurrentElem() instanceof final TDOI tdoi) { + return currentElem.getName().equals(CommonConstants.STVAL) && tdoi.getName().equals(CommonConstants.MOD_DO_NAME); + } + return false; } public AbstractDAIAdapter update(Map daiValues) throws ScdException { - if(daiValues.size() > 1 && daiValues.containsKey(0L)){ + if (daiValues.size() > 1 && daiValues.containsKey(0L)) { update(0L, daiValues.get(0L)); // to be refined (with COMPAS TEAMS) } else { for (Map.Entry mapVal : daiValues.entrySet()) { @@ -105,47 +105,55 @@ public AbstractDAIAdapter update(Map /** * Updates DAI SGroup value + * * @param sGroup SGroup to update - * @param val value + * @param val value * @throws ScdException throws when DAI for which SGroup should be updated is not updatable */ public void update(Long sGroup, String val) throws ScdException { - if(currentElem.isSetValImport() && !currentElem.isValImport()){ + if (!isDOModDAstVal() && currentElem.isSetValImport() && !currentElem.isValImport()) { String msg = String.format( - "DAI(%s) cannot be updated : valImport(false)",currentElem.getName() + "DAI(%s) cannot be updated : valImport(false) %s", currentElem.getName(), getXPath() ); throw new ScdException(msg); } - Stream tValStream = currentElem.getVal().stream(); if (sGroup != null && sGroup != 0) { - Optional tVal = tValStream.filter(tValElem -> tValElem.isSetSGroup() && - sGroup.equals(tValElem.getSGroup())) - .findFirst(); - if(tVal.isPresent()){ - tVal.get().setValue(val); - } else { - TVal newTVal = new TVal(); - newTVal.setValue(val); - newTVal.setSGroup(sGroup); - currentElem.getVal().add(newTVal); - } + updateSGroupVal(sGroup, val); } else { - Optional tVal = tValStream.findFirst(); - if(tVal.isPresent()){ - tVal.get().setValue(val); - }else { - TVal newTVal = new TVal(); - newTVal.setValue(val); - currentElem.getVal().add(newTVal); - } + updateVal(val); } } - public IDataAdapter addDAI(String name){ + private void updateSGroupVal(Long sGroup, String val) { + currentElem.getVal().stream() + .filter(tValElem -> tValElem.isSetSGroup() && sGroup.equals(tValElem.getSGroup())) + .findFirst() + .orElseGet( + () -> { + TVal newTVal = new TVal(); + newTVal.setSGroup(sGroup); + currentElem.getVal().add(newTVal); + return newTVal; + }) + .setValue(val); + } + + public void updateVal(String s) { + currentElem.getVal().stream().findFirst() + .orElseGet( + () -> { + TVal newTVal = new TVal(); + currentElem.getVal().add(newTVal); + return newTVal; + }) + .setValue(s); + } + + public IDataAdapter addDAI(String name) { throw new UnsupportedOperationException("DAI cannot contain an SDI"); } - public IDataAdapter addSDOI(String sdoName){ + public IDataAdapter addSDOI(String sdoName) { throw new UnsupportedOperationException("DAI cannot contain an DAI"); } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractLNAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractLNAdapter.java index 89ab101f5..bc79c4b94 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractLNAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AbstractLNAdapter.java @@ -705,46 +705,40 @@ public void updateDAI(@NonNull ResumedDataTemplate rDtt) throws ScdException { DAITracker.MatchResult matchResult = daiTracker.search(); AbstractDAIAdapter daiAdapter = null; IDataParentAdapter doiOrSdoiAdapter; - if (matchResult == DAITracker.MatchResult.FULL_MATCH) { - // update - daiAdapter = (AbstractDAIAdapter) daiTracker.getBdaiOrDaiAdapter(); - if ((daiAdapter.isValImport() != null && daiAdapter.isValImport()) || - (daiAdapter.isValImport() == null && rDtt.isUpdatable())) { - daiAdapter.update(daTypeName.getDaiValues()); - return; - } else { - throw new ScdException(String.format("DAI (%s -%s) cannot be updated", doTypeName, daTypeName)); - } - } - if (rDtt.isUpdatable()) { + if (!rDtt.isUpdatable()) + return; + + if (rDtt.isUpdatable() && matchResult == DAITracker.MatchResult.FULL_MATCH) { + daiAdapter = (AbstractDAIAdapter) daiTracker.getBdaiOrDaiAdapter(); + } else { doiOrSdoiAdapter = daiTracker.getDoiOrSdoiAdapter(); - int idx = daiTracker.getIndexDoType(); + int indexDoType = daiTracker.getIndexDoType(); int doSz = doTypeName.getStructNames().size(); if (matchResult == DAITracker.MatchResult.FAILED) { doiOrSdoiAdapter = addDOI(doTypeName.getName()); - idx = 0; - } else if (idx == -1) { - idx = 0; - } else if (idx == doSz - 1) { - idx = doSz; + indexDoType = 0; + } else if (indexDoType == -1) { + indexDoType = 0; + } else if (indexDoType == doSz - 1) { + indexDoType = doSz; } - for (int i = idx; i < doSz; ++i) { + for (int i = indexDoType; i < doSz; ++i) { String sdoName = doTypeName.getStructNames().get(i); doiOrSdoiAdapter = doiOrSdoiAdapter.addSDOI(sdoName); } IDataParentAdapter daiOrBdaiAdapter = daiTracker.getDoiOrSdoiAdapter(); - idx = daiTracker.getIndexDaType(); + int indexDaType = daiTracker.getIndexDaType(); int daSz = daTypeName.getStructNames().size(); - if (idx <= -1) { - idx = 0; - } else if (idx == daSz - 1) { - idx = daSz; + if (indexDaType <= -1) { + indexDaType = 0; + } else if (indexDaType == daSz - 1) { + indexDaType = daSz; } - for (int i = idx; i < daSz; ++i) { + for (int i = indexDaType; i < daSz; ++i) { String bdaName = daTypeName.getStructNames().get(i); - if (idx == 0) { + if (indexDaType == 0) { daiOrBdaiAdapter = doiOrSdoiAdapter.addSDOI(daTypeName.getName()); } else if (i == daSz - 1) { daiAdapter = daiOrBdaiAdapter.addDAI(bdaName, rDtt.isUpdatable()); @@ -755,9 +749,8 @@ public void updateDAI(@NonNull ResumedDataTemplate rDtt) throws ScdException { if (daiAdapter == null) { daiAdapter = doiOrSdoiAdapter.addDAI(daTypeName.getName(), rDtt.isUpdatable()); } - - daiAdapter.update(daTypeName.getDaiValues()); } + daiAdapter.update(daTypeName.getDaiValues()); } /** @@ -961,14 +954,31 @@ public Optional findControlBlock(String name, ControlBlockE public ControlBlockAdapter createControlBlockIfNotExists(String cbName, String id, String datSet, ControlBlockEnum controlBlockEnum) { return findControlBlock(cbName, controlBlockEnum) - .orElseGet(() -> addControlBlock( - switch (controlBlockEnum) { - case GSE -> new GooseControlBlock(cbName, id, datSet); - case SAMPLED_VALUE -> new SMVControlBlock(cbName, id, datSet); - case REPORT -> new ReportControlBlock(cbName, id, datSet); - default -> throw new IllegalArgumentException("Unsupported ControlBlock Type " + controlBlockEnum); - } - ) - ); + .orElseGet(() -> addControlBlock( + switch (controlBlockEnum) { + case GSE -> new GooseControlBlock(cbName, id, datSet); + case SAMPLED_VALUE -> new SMVControlBlock(cbName, id, datSet); + case REPORT -> new ReportControlBlock(cbName, id, datSet); + default -> throw new IllegalArgumentException("Unsupported ControlBlock Type " + controlBlockEnum); + } + ) + ); + } + + /** + * Finds all FCDAs in DataSet of Control Block feeding ExtRef + * @param tExtRef Fed ExtRef + * @return list of all FCDA in DataSet of Control Block + */ + public List getFCDAs(TExtRef tExtRef){ + TControl tControl = getTControlsByType(ControlBlockEnum.from(tExtRef.getServiceType()).getControlBlockClass()).stream() + .filter(tCtrl -> tExtRef.getSrcCBName() != null && tExtRef.getSrcCBName().equals(tCtrl.getName())) + .findFirst().orElseThrow(() -> + new ScdException(String.format("Control Block %s not found in %s", tExtRef.getSrcCBName(), getXPath()))); + return getCurrentElem().getDataSet().stream() + .filter(tDataSet -> tDataSet.getName().equals(tControl.getDatSet())) + .map(TDataSet::getFCDA) + .flatMap(Collection::stream) + .toList(); } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java new file mode 100644 index 000000000..4e12b6658 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapter.java @@ -0,0 +1,340 @@ +/* + * // SPDX-FileCopyrightText: 2023 RTE FRANCE + * // + * // SPDX-License-Identifier: Apache-2.0 + */ + +package org.lfenergy.compas.sct.commons.scl.ied; + +import org.lfenergy.compas.scl2007b4.model.*; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; +import org.lfenergy.compas.sct.commons.exception.ScdException; +import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; +import org.lfenergy.compas.sct.commons.util.ServicesConfigEnum; +import org.lfenergy.compas.sct.commons.util.Utils; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * A representation of the model object + * {@link org.lfenergy.compas.scl2007b4.model.TAccessPoint AccessPoint}. + *

    + * The following features are supported: + *

    + *
      + *
    1. Functions
    2. + *
        + *
      • {@link AccessPointAdapter#checkFCDALimitations Returns the value of the name attribute}
      • + *
      • {@link AccessPointAdapter#checkControlsLimitation Returns the value of the Service object}
      • + *
      + *
    + */ + +public class AccessPointAdapter extends SclElementAdapter { + + public static final long MAX_OCCURRENCE_NO_LIMIT_VALUE = -1L; + private static final String CLIENT_IED_NAME = "The Client IED "; + + /** + * Constructor + * + * @param parentAdapter Parent container reference + * @param tAccessPoint Current reference + */ + public AccessPointAdapter(IEDAdapter parentAdapter, TAccessPoint tAccessPoint) { + super(parentAdapter, tAccessPoint); + } + + /** + * Check if current element is a child of the parent element + * + * @return true if the currentElem is part of the parentAdapter children + */ + @Override + protected boolean amChildElementRef() { + return parentAdapter.getCurrentElem().getAccessPoint().stream() + .anyMatch(tAccessPoint -> tAccessPoint.getName().equals(currentElem.getName())); + } + + @Override + protected String elementXPath() { + return String.format("AccessPoint[%s]", Utils.xpathAttributeFilter("name", currentElem.isSetName() ? currentElem.getName() : null)); + } + + /** + * Gets all LDevice from AccessPoint + * + * @return Stream of LDeviceAdapter object as IEDs of SCL + */ + private Stream streamLDeviceAdapters() { + if (!currentElem.isSetServer()) return Stream.empty(); + return currentElem.getServer().getLDevice().stream() + .map(tlDevice -> new LDeviceAdapter(getParentAdapter(), tlDevice)); + } + + /** + * Checks FCDA number limitation into each LDevice of AccessPoint + * + * @return List of errors encountered for LDevices + */ + public List checkFCDALimitations() { + long max = getMaxInstanceAuthorized(ServicesConfigEnum.FCDA); + if (currentElem.getServer() == null || max == MAX_OCCURRENCE_NO_LIMIT_VALUE) return Collections.emptyList(); + return currentElem.getServer().getLDevice().stream() + .map(tlDevice -> new LDeviceAdapter(parentAdapter, tlDevice)) + .map(lDeviceAdapter -> + lDeviceAdapter.getLNAdaptersIncludingLN0().stream() + .map(abstractLNAdapter -> abstractLNAdapter.getCurrentElem().getDataSet()) + .flatMap(Collection::stream) + .filter(tDataSet -> tDataSet.getFCDA().size() > max) + .map(tDataSet -> SclReportItem.fatal(getXPath(), String.format("There are too much FCDA for the DataSet %S for the LDevice %S in IED %S", + tDataSet.getName(), lDeviceAdapter.getInst(), parentAdapter.getName()))).toList() + ).flatMap(Collection::stream).toList(); + } + + /** + * Checks if occurrences of specified tpe (DataSet, Controls) exceeds config limitation + * + * @param servicesConfigEnum type of element for which limitation is checked + * @param msg error message to display + * @return Optional of encountered error or empty + */ + public Optional checkControlsLimitation(ServicesConfigEnum servicesConfigEnum, String msg) { + long max = getMaxInstanceAuthorized(servicesConfigEnum); + long value = getNumberOfItems(servicesConfigEnum); + return max == MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.fatal(getXPath(), String.format("%s %s", msg, parentAdapter.getName()))); + } + + /** + * Counts all occurrence of Control into AccessPoint + * + * @param servicesConfigEnum type (GOOSE, Report, SampledValue, DataSet) + * @return number of occurrence + */ + private long getNumberOfItems(ServicesConfigEnum servicesConfigEnum) { + if (!currentElem.isSetServer()) return 0L; + return currentElem.getServer().getLDevice().stream() + .map(tlDevice -> new LDeviceAdapter(parentAdapter, tlDevice)) + .map(lDeviceAdapter -> { + List> list = new ArrayList<>(); + if (servicesConfigEnum == ServicesConfigEnum.GSE || servicesConfigEnum == ServicesConfigEnum.SMV) + list.add(lDeviceAdapter.getLN0Adapter()); + else list.addAll(lDeviceAdapter.getLNAdaptersIncludingLN0()); + return list; + }) + .flatMap(Collection::stream) + .map(abstractLNAdapter -> { + if (servicesConfigEnum == ServicesConfigEnum.DATASET) { + return abstractLNAdapter.getCurrentElem().getDataSet(); + } else { + return abstractLNAdapter.getTControlsByType(getControlTypeClass(servicesConfigEnum)); + } + }) + .mapToLong(Collection::size).sum(); + } + + private Class getControlTypeClass(ServicesConfigEnum servicesConfigEnum) { + return switch (servicesConfigEnum) { + case REPORT -> TReportControl.class; + case GSE -> TGSEControl.class; + case SMV -> TSampledValueControl.class; + default -> throw new ScdException("Unknown Control Block Type: " + servicesConfigEnum); + }; + } + + /** + * Gets max number authorized in configuration of each element DataSets, FCDAs, Control Blocks) into an AccessPoint + * + * @param servicesConfigEnum element type + * @return max number authorized by config + */ + private long getMaxInstanceAuthorized(ServicesConfigEnum servicesConfigEnum) { + if (currentElem.getServices() == null || currentElem.getServices().getConfDataSet() == null) + return MAX_OCCURRENCE_NO_LIMIT_VALUE; + TServices tServices = currentElem.getServices(); + + return switch (servicesConfigEnum) { + case DATASET -> + tServices.getConfDataSet().isSetMax() ? tServices.getConfDataSet().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case FCDA -> + tServices.getConfDataSet().isSetMaxAttributes() ? tServices.getConfDataSet().getMaxAttributes() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case REPORT -> + tServices.getConfReportControl().isSetMax() ? tServices.getConfReportControl().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case GSE -> tServices.getGOOSE().isSetMax() ? tServices.getGOOSE().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + case SMV -> tServices.getSMVsc().isSetMax() ? tServices.getSMVsc().getMax() : MAX_OCCURRENCE_NO_LIMIT_VALUE; + }; + } + + + /** + * Checks FCDA number limitation for binded IED + * + * @param msg message to display hen error occurred + * @return Optional of encountered error or empty + */ + public Optional checkLimitationForBoundIEDFCDAs(List tExtRefs, String msg) { + long value = tExtRefs.stream() + .map(tExtRef -> { + IEDAdapter iedAdapter = getParentAdapter().getParentAdapter().getIEDAdapterByName(tExtRef.getIedName()); + LDeviceAdapter lDeviceAdapter; + if (tExtRef.getSrcLDInst() != null) { + lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(tExtRef.getSrcLDInst()); + } else { + lDeviceAdapter = iedAdapter.getLDeviceAdapterByLdInst(tExtRef.getLdInst()); + } + AbstractLNAdapter abstractLNAdapter; + if (!tExtRef.isSetSrcLNClass() || tExtRef.getSrcLNClass().contains(TLLN0Enum.LLN_0.value())) { + abstractLNAdapter = lDeviceAdapter.getLN0Adapter(); + } else { + abstractLNAdapter = lDeviceAdapter.getLNAdapter(tExtRef.getSrcLNClass().get(0), tExtRef.getSrcLNInst(), tExtRef.getSrcPrefix()); + } + return abstractLNAdapter.getFCDAs(tExtRef); + }) + .flatMap(Collection::stream) + .toList() + .size(); + long max; + if (currentElem.getServices() == null) { + max = AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + } else { + TClientServices tClientServices = currentElem.getServices().getClientServices(); + max = tClientServices != null && tClientServices.isSetMaxAttributes() ? tClientServices.getMaxAttributes() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + } + + return max == AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : + Optional.of(SclReportItem.fatal(getParentAdapter().getXPath(), msg)); + + } + + /** + * @param sclReportItems + * @param tExtRefs + */ + record ExtRefAnalyzeRecord(List sclReportItems, List tExtRefs) { + } + + /** + * Returns all ExtRef of the AccessPoint which have SrcCBName set and ServiceType provided + * + * @return ExtRefAnalyzeRecord object containing Set of ExtRefs and errors list for ExtRefs with SrcCBName and without ServiceType provided + */ + public ExtRefAnalyzeRecord getAllCoherentExtRefForAnalyze() { + List sclReportItems = new ArrayList<>(); + List tExtRefList = new ArrayList<>(); + streamLDeviceAdapters().map(lDeviceAdapter -> { + List extRefs = lDeviceAdapter.getLN0Adapter().getExtRefs().stream().filter(TExtRef::isSetSrcCBName).collect(Collectors.toCollection(ArrayList::new)); + sclReportItems.addAll(checkExtRefWithoutServiceType(extRefs, lDeviceAdapter.getXPath())); + extRefs.removeIf(tExtRef -> !tExtRef.isSetServiceType()); + return extRefs; + }).flatMap(Collection::stream).forEach(tExtRef -> { + if (tExtRefList.isEmpty()) + tExtRefList.add(tExtRef); + else { + if (tExtRefList.stream().noneMatch(t -> isExtRefFeedBySameControlBlock(tExtRef, t))) + tExtRefList.add(tExtRef); + } + }); + return new ExtRefAnalyzeRecord(sclReportItems, tExtRefList); + } + + /** + * Checks all ExtRefs with SrcCBName and without ServiceType provided + * + * @param tExtRefs Set of ExtRefs to check + * @return errors list + */ + private List checkExtRefWithoutServiceType(List tExtRefs, String xPath) { + return tExtRefs.stream() + .filter(tExtRef -> !tExtRef.isSetServiceType()) + .map(tExtRef -> + SclReportItem.fatal(xPath, "ExtRef signal without ServiceType : " + tExtRef.getDesc())) + .toList(); + } + + /** + * Checks if two ExtRefs fed by same Control Block for SCL limits analyze + * Nota : this equality is only for checking limitation check + * + * @param t1 extref to compare + * @param t2 extref to compare + * @return true if the two ExtRef are fed by same Control Block, otherwise false + */ + private static boolean isExtRefFeedBySameControlBlock(TExtRef t1, TExtRef t2) { + String srcLNClass1 = (t1.isSetSrcLNClass()) ? t1.getSrcLNClass().get(0) : TLLN0Enum.LLN_0.value(); + String srcLNClass2 = (t2.isSetSrcLNClass()) ? t2.getSrcLNClass().get(0) : TLLN0Enum.LLN_0.value(); + return Utils.equalsOrBothBlank(t1.getIedName(), t2.getIedName()) + && Utils.equalsOrBothBlank(t1.getSrcLDInst(), t2.getSrcLDInst()) + && srcLNClass1.equals(srcLNClass2) + && Utils.equalsOrBothBlank(t1.getSrcLNInst(), t2.getSrcLNInst()) + && Utils.equalsOrBothBlank(t1.getSrcPrefix(), t2.getSrcPrefix()) + && t1.getServiceType().equals(t2.getServiceType()); + } + + /** + * Checks Control Blocks (Report, Goose, SMV) number limitation for binded IED + * + * @return List of errors encountered + */ + public List checkLimitationForBoundIEDControls(List tExtRefs) { + Map> extRefsByServiceType = tExtRefs.stream() + .filter(TExtRef::isSetServiceType) + .collect(Collectors.groupingBy(TExtRef::getServiceType, Collectors.toSet())); + return extRefsByServiceType.keySet().stream() + .map(tServiceType -> { + Set tExtRefSet = extRefsByServiceType.get(tServiceType); + return switch (tServiceType) { + case REPORT -> + checkLimitationForOneControlType(tExtRefSet, CLIENT_IED_NAME + getParentAdapter().getName() + " subscribes to too much REPORT Control Blocks.", ServicesConfigEnum.REPORT); + case GOOSE -> + checkLimitationForOneControlType(tExtRefSet, CLIENT_IED_NAME + getParentAdapter().getName() + " subscribes to too much GOOSE Control Blocks.", ServicesConfigEnum.GSE); + case SMV -> + checkLimitationForOneControlType(tExtRefSet, CLIENT_IED_NAME + getParentAdapter().getName() + " subscribes to too much SMV Control Blocks.", ServicesConfigEnum.SMV); + default -> throw new ScdException("Unsupported value: " + tServiceType); + }; + }).flatMap(Optional::stream) + .toList(); + } + + /** + * Checks Control Block number limitation for binded IED + * + * @param tExtRefs list of ExtRefs referenced same ied + * @param msg message to display hen error occured + * @param servicesConfigEnum type of Control Block for which check is done + * @return Optional of encountered error or empty + */ + private Optional checkLimitationForOneControlType(Set tExtRefs, String msg, ServicesConfigEnum servicesConfigEnum) { + long max = getMaxInstanceAuthorizedForBoundIED(servicesConfigEnum); + long value = tExtRefs.size(); + return max == AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE || value <= max ? Optional.empty() : Optional.of(SclReportItem.fatal(getParentAdapter().getXPath(), msg)); + } + + /** + * Gets max number authorized in configuration of each element ( DataSets, FCDAs, Control Blocks) into an AccessPoint + * + * @param servicesConfigEnum element type + * @return max number authorized by config + */ + private long getMaxInstanceAuthorizedForBoundIED(ServicesConfigEnum servicesConfigEnum) { + if (currentElem.getServices() == null || currentElem.getServices().getClientServices() == null) { + return AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + } + TClientServices tClientServices = currentElem.getServices().getClientServices(); + return switch (servicesConfigEnum) { + case FCDA -> + tClientServices.isSetMaxAttributes() ? tClientServices.getMaxAttributes() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + case REPORT -> + tClientServices.isSetMaxReports() ? tClientServices.getMaxReports() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + case GSE -> + tClientServices.isSetMaxGOOSE() ? tClientServices.getMaxGOOSE() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + case SMV -> + tClientServices.isSetMaxSMV() ? tClientServices.getMaxSMV() : AccessPointAdapter.MAX_OCCURRENCE_NO_LIMIT_VALUE; + default -> throw new ScdException("Unsupported value: " + servicesConfigEnum); + }; + + } + + +} diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapter.java index ecd34e231..a12c63ec4 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapter.java @@ -4,14 +4,19 @@ package org.lfenergy.compas.sct.commons.scl.ied; -import org.lfenergy.compas.scl2007b4.model.TAnyLN; -import org.lfenergy.compas.scl2007b4.model.TDAI; -import org.lfenergy.compas.scl2007b4.model.TDOI; -import org.lfenergy.compas.scl2007b4.model.TSDI; +import org.lfenergy.compas.scl2007b4.model.*; +import org.lfenergy.compas.sct.commons.dto.ExtrefTarget; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.exception.ScdException; +import org.lfenergy.compas.sct.commons.scl.ObjectReference; import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; import org.lfenergy.compas.sct.commons.util.Utils; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + /** * A representation of the model object @@ -39,10 +44,18 @@ */ public class DOIAdapter extends SclElementAdapter, TDOI> implements IDataParentAdapter { + protected static final String DA_NAME_SET_SRC_REF = "setSrcRef"; + protected static final String DA_NAME_SET_SRC_CB = "setSrcCB"; + protected static final String DA_NAME_SET_TST_REF = "setTstRef"; + protected static final String DA_NAME_SET_TST_CB = "setTstCB"; + + private static final Comparator EXTREF_DESC_SUFFIX_COMPARATOR = Comparator.comparingInt(extRef -> extractDescSuffix(extRef.getDesc())); + /** * Constructor + * * @param parentAdapter Parent container reference - * @param currentElem Current reference + * @param currentElem Current reference */ protected DOIAdapter(AbstractLNAdapter parentAdapter, TDOI currentElem) { super(parentAdapter, currentElem); @@ -50,6 +63,7 @@ protected DOIAdapter(AbstractLNAdapter parentAdapter, TDOI cur /** * Check if node is child of the reference node + * * @return link parent child existence */ @Override @@ -65,6 +79,7 @@ protected String elementXPath() { /** * Gets SDI by name from current DOI + * * @param sName name of SDI to get * @return RootSDIAdapter object * @throws ScdException throws when specified name of SDI not present in current DOI @@ -76,40 +91,52 @@ public RootSDIAdapter getStructuredDataAdapterByName(String sName) throws ScdExc .filter(tUnNaming -> tUnNaming.getClass().equals(TSDI.class)) .map(TSDI.class::cast) .filter(tsdi -> tsdi.getName().equals(sName)) - .map(tsdi -> new RootSDIAdapter(this,tsdi)) + .map(tsdi -> new RootSDIAdapter(this, tsdi)) .findFirst() .orElseThrow( - ()-> new ScdException( - String.format("Unknown SDI (%s) in DOI (%s)", sName, currentElem.getName()) - ) + () -> new ScdException( + String.format("Unknown SDI (%s) in DOI (%s)", sName, currentElem.getName()) + ) ); } /** * Gets DAI from current DOI + * * @param daName name of DAI to get * @return DAIAdapter object * @throws ScdException throws when specified name of DAI not present in current DOI */ @Override public DAIAdapter getDataAdapterByName(String daName) throws ScdException { - return currentElem.getSDIOrDAI() + return findDataAdapterByName(daName) + .orElseThrow( + () -> new ScdException( + String.format("Unknown DAI (%s) in DOI (%s)", daName, currentElem.getName()) + ) + ); + } + + /** + * Given a DAI[name], returns an associated DAIAdapter + * + * @param daName name of DAI to get + * @return an Optional DAIAdapter if found, Optional.empty() otherwise + */ + public Optional findDataAdapterByName(String daName) { + return currentElem.getSDIOrDAI() .stream() .filter(tUnNaming -> tUnNaming.getClass().equals(TDAI.class)) .map(TDAI.class::cast) .filter(tdai -> tdai.getName().equals(daName)) - .map(tdai -> new DAIAdapter(this,tdai)) - .findFirst() - .orElseThrow( - ()-> new ScdException( - String.format("Unknown DAI (%s) in DOI (%s)", daName, currentElem.getName()) - ) - ); + .map(tdai -> new DAIAdapter(this, tdai)) + .findFirst(); } /** * Adds DAI to current DOI - * @param name name of DAI to add + * + * @param name name of DAI to add * @param isUpdatable updatability state of DAI * @return DAIAdapter object as added DAI */ @@ -119,11 +146,12 @@ public DAIAdapter addDAI(String name, boolean isUpdatable) { tdai.setName(name); tdai.setValImport(isUpdatable); currentElem.getSDIOrDAI().add(tdai); - return new DAIAdapter(this,tdai); + return new DAIAdapter(this, tdai); } /** * Adds SDOI to SDI in current DOI + * * @param sdoName name of SDOI to add * @return RootSDIAdapter object as added SDOI */ @@ -132,7 +160,64 @@ public RootSDIAdapter addSDOI(String sdoName) { TSDI tsdi = new TSDI(); tsdi.setName(sdoName); currentElem.getSDIOrDAI().add(tsdi); - return new RootSDIAdapter(this,tsdi); + return new RootSDIAdapter(this, tsdi); + } + + /** + * Update the DAI/Val according to the ExtRef attributes. + * If the ExtRef.desc start with DAI[name='purpose']/Val and end with "_1" (nominal case): + * - DAI[name='setSrcRef']/Val is updated with ExtRef attributes concatenation + * - DAI[name='setSrcCB']/Val is updated with ExtRef attributes concatenation if ExtRef.srcCBName is present + * If the ExtRef.desc start with DAI[name='purpose']/Val and end with "_2" or greater (test case): + * - DAI[name='setTstRef']/Val is updated with ExtRef attributes concatenation + * - DAI[name='setTstCB']/Val is updated with ExtRef attributes concatenation if ExtRef.srcCBName is present + * + * @param tExtRefs all the ExtRefs contained in the current LDevice/LLN0 + * @return a filled SclReportItem if an error occurs, empty SclReportItem otherwise + */ + public Optional updateDaiFromExtRef(List tExtRefs) { + Optional tExtRefMinOptional = tExtRefs.stream().min(EXTREF_DESC_SUFFIX_COMPARATOR); + + if (tExtRefMinOptional.isPresent() && extractDescSuffix(tExtRefMinOptional.get().getDesc()) == 1) { + TExtRef tExtRefMin = tExtRefMinOptional.get(); + findDataAdapterByName(DA_NAME_SET_SRC_REF) + .orElse(addDAI(DA_NAME_SET_SRC_REF, true)) + .updateVal(createInRefValNominalString(tExtRefMin)); + if (tExtRefMin.isSetSrcCBName()) { + findDataAdapterByName(DA_NAME_SET_SRC_CB) + .orElse(addDAI(DA_NAME_SET_SRC_CB, true)) + .updateVal(createInRefValTestString(tExtRefMin)); + } + + Optional tExtRefMaxOptional = tExtRefs.stream().max(Comparator.comparingInt(o -> Integer.parseInt(Objects.requireNonNull(Utils.extractField(o.getDesc(), "_", -1))))); + if (tExtRefMaxOptional.isPresent() && extractDescSuffix(tExtRefMaxOptional.get().getDesc()) > 1) { + TExtRef tExtRefMax = tExtRefMaxOptional.get(); + findDataAdapterByName(DA_NAME_SET_TST_REF) + .orElse(addDAI(DA_NAME_SET_TST_REF, true)) + .updateVal(createInRefValNominalString(tExtRefMax)); + if (tExtRefMax.isSetSrcCBName()) { + findDataAdapterByName(DA_NAME_SET_TST_CB) + .orElse(addDAI(DA_NAME_SET_TST_CB, true)) + .updateVal(createInRefValTestString(tExtRefMax)); + } + } + } else { + return Optional.of(SclReportItem.warning(getXPath(), "The DOI %s can't be bound with an ExtRef".formatted(getXPath()))); + } + + return Optional.empty(); + } + + private static int extractDescSuffix(String desc) throws NumberFormatException { + return Integer.parseInt(Utils.extractField(desc, "_", -1)); + } + + private String createInRefValNominalString(TExtRef extRef) { + return new ObjectReference(extRef, ExtrefTarget.SRC_REF).getReference(); + } + + private String createInRefValTestString(TExtRef extRef) { + return new ObjectReference(extRef, ExtrefTarget.SRC_CB).getReference(); } /** diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapter.java index 542c9887a..df3934aac 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapter.java @@ -9,17 +9,16 @@ import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.ExtRefBindingInfo; import org.lfenergy.compas.sct.commons.dto.ExtRefSignalInfo; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.scl.ObjectReference; import org.lfenergy.compas.sct.commons.scl.PrivateService; import org.lfenergy.compas.sct.commons.scl.SclElementAdapter; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; +import org.lfenergy.compas.sct.commons.util.ServicesConfigEnum; import org.lfenergy.compas.sct.commons.util.Utils; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.stream.Stream; /** @@ -320,4 +319,48 @@ public Optional getPrivateHeader(String privateType) { public Optional getPrivateCompasBay() { return PrivateService.extractCompasPrivate(currentElem, TCompasBay.class); } + + /** + * Checks if Controls, DataSets and FCDAs of IED respect config limitation + * @return empty list if all IED respect limits, otherwise list of errors + */ + public List checkDataGroupCoherence() { + return currentElem.getAccessPoint().stream() + .map(tAccessPoint -> { + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(this, tAccessPoint); + List sclReportItems = new ArrayList<>(accessPointAdapter.checkFCDALimitations()); + accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.DATASET, "There are too much DataSets for the IED") + .ifPresent(sclReportItems::add); + accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.REPORT, "There are too much Report Control Blocks for the IED") + .ifPresent(sclReportItems::add); + accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.GSE, "There are too much GOOSE Control Blocks for the IED") + .ifPresent(sclReportItems::add); + accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.SMV, "There are too much SMV Control Blocks for the IED") + .ifPresent(sclReportItems::add); + return sclReportItems; + }).flatMap(Collection::stream) + .toList(); + } + + /** + * Checks if Controls and FCDAs of source IEDs respect config limitation + * @return empty list if all IED respect limits, otherwise list of errors + */ + public List checkBindingDataGroupCoherence() { + return currentElem.getAccessPoint().stream() + .map(tAccessPoint -> { + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(this, tAccessPoint); + AccessPointAdapter.ExtRefAnalyzeRecord extRefAnalyzeRecord = accessPointAdapter.getAllCoherentExtRefForAnalyze(); + List sclReportItems = new ArrayList<>(extRefAnalyzeRecord.sclReportItems()); + accessPointAdapter.checkLimitationForBoundIEDFCDAs(extRefAnalyzeRecord.tExtRefs(), "There are too much FCDA for the Client IED " + getName()) + .ifPresent(sclReportItems::add); + sclReportItems.addAll(accessPointAdapter.checkLimitationForBoundIEDControls(extRefAnalyzeRecord.tExtRefs())); + return sclReportItems; + }).flatMap(List::stream).toList(); + + } + + + + } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java index b2e5f33e2..0f0496fc1 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapter.java @@ -420,4 +420,5 @@ private IEDAdapter getIedAdapter() { private SclRootAdapter getSclRootAdapter() { return getIedAdapter().getParentAdapter(); } + } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LN0Adapter.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LN0Adapter.java index efc341581..e3399ad5d 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LN0Adapter.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/LN0Adapter.java @@ -61,6 +61,7 @@ * LDName = "name" attribute of IEDName element + "inst" attribute of LDevice element * LNName = "prefix" + "lnClass" + "lnInst" * + * * @see org.lfenergy.compas.scl2007b4.model.TLN0 * @see org.lfenergy.compas.sct.commons.scl.ied.AbstractLNAdapter */ @@ -70,17 +71,22 @@ public class LN0Adapter extends AbstractLNAdapter { public static final DaTypeName STVAL_DA_TYPE_NAME = new DaTypeName(STVAL); public static final DoTypeName BEHAVIOUR_DO_TYPE_NAME = new DoTypeName(BEHAVIOUR_DO_NAME); public static final DaTypeName BEHAVIOUR_DA_TYPE_NAME = getDaTypeNameForBeh(); + private static final String DAI_NAME_PURPOSE = "purpose"; + private static final String INREF_PREFIX = "InRef"; + /** * Constructor + * * @param parentAdapter Parent container reference - * @param ln0 Current reference + * @param ln0 Current reference */ public LN0Adapter(LDeviceAdapter parentAdapter, LN0 ln0) { - super(parentAdapter,ln0); + super(parentAdapter, ln0); } /** * Check if node is child of the reference node + * * @return link parent child existence */ @Override @@ -96,6 +102,7 @@ protected String elementXPath() { /** * Gets current LN0 class type + * * @return LN0.class */ @Override @@ -105,6 +112,7 @@ protected Class getElementClassType() { /** * Gets LNClass enum value of current LNO + * * @return LNClass value */ public String getLNClass() { @@ -113,6 +121,7 @@ public String getLNClass() { /** * Gets LNInst value of current LN0 + * * @return "" */ @Override @@ -122,6 +131,7 @@ public String getLNInst() { /** * Gets Prefix value of current LN0 + * * @return "" */ @Override @@ -131,19 +141,22 @@ public String getPrefix() { /** * Gets Inputs node as an adapter + * * @return an InputsAdapter */ - public InputsAdapter getInputsAdapter(){ + public InputsAdapter getInputsAdapter() { return new InputsAdapter(this, currentElem.getInputs()); } - /** Checks if given attibrute corresponds to DataSet or ReportControl or SMVControl or GSEControl in current LN0 + /** + * Checks if given attibrute corresponds to DataSet or ReportControl or SMVControl or GSEControl in current LN0 + * * @param dataAttribute attribute to check * @return Boolean value of check result */ @Override - protected boolean matchesDataAttributes(String dataAttribute){ - return super.matchesDataAttributes(dataAttribute) || + protected boolean matchesDataAttributes(String dataAttribute) { + return super.matchesDataAttributes(dataAttribute) || currentElem.getSampledValueControl().stream().anyMatch(smp -> smp.getName().equals(dataAttribute)) || currentElem.getGSEControl().stream().anyMatch(gse -> gse.getName().equals(dataAttribute)); } @@ -178,6 +191,7 @@ private ResumedDataTemplate createDaiFilter(DoTypeName doTypeName, DaTypeName da /** * Verify and update LDevice status in parent Node + * * @param iedNameLDeviceInstList pair of Ied name and LDevice inst attributes * @return Set of Errors */ @@ -186,7 +200,7 @@ public Optional updateLDeviceStatus(List> ie final String iedName = getParentAdapter().getParentAdapter().getName(); final String ldInst = getParentAdapter().getInst(); ResumedDataTemplate daiBehFilter = createDaiFilter(BEHAVIOUR_DO_TYPE_NAME, BEHAVIOUR_DA_TYPE_NAME); - List daiBehList = getDAI(daiBehFilter, false); + List daiBehList = getDAI(daiBehFilter, false); if (daiBehList.isEmpty()) { return Optional.of(buildFatalReportItem("The LDevice doesn't have a DO @name='Beh' OR its associated DA@fc='ST' AND DA@name='stVal'")); } @@ -206,13 +220,13 @@ public Optional updateLDeviceStatus(List> ie ResumedDataTemplate newDaModToSetInLN0 = optionalModStVal.get(); String initialValue = newDaModToSetInLN0.findFirstValue().orElse(""); lDeviceActivation.checkLDeviceActivationStatus(iedName, ldInst, compasLDeviceStatus, enumValues); - if(lDeviceActivation.isUpdatable()){ - if(!initialValue.equals(lDeviceActivation.getNewVal())) { + if (lDeviceActivation.isUpdatable()) { + if (!initialValue.equals(lDeviceActivation.getNewVal())) { newDaModToSetInLN0.setVal(lDeviceActivation.getNewVal()); updateDAI(newDaModToSetInLN0); } - }else { - if(lDeviceActivation.getErrorMessage() != null) { + } else { + if (lDeviceActivation.getErrorMessage() != null) { return Optional.of(buildFatalReportItem(lDeviceActivation.getErrorMessage())); } } @@ -221,13 +235,13 @@ public Optional updateLDeviceStatus(List> ie public Optional getLDeviceStatus() { return getDaiModStVal() - .flatMap(ResumedDataTemplate::findFirstValue); + .flatMap(ResumedDataTemplate::findFirstValue); } private Optional getDaiModStVal() { ResumedDataTemplate daiModFilter = createDaiFilter(MOD_DO_TYPE_NAME, STVAL_DA_TYPE_NAME); return getDAI(daiModFilter, false).stream() - .findFirst(); + .findFirst(); } private static DaTypeName getDaTypeNameForBeh() { @@ -237,4 +251,28 @@ private static DaTypeName getDaTypeNameForBeh() { daTypeNameBeh.setFc(TFCEnum.ST); return daTypeNameBeh; } + + /** + * Update DAIs of DO InRef in all LN0 of the SCD using matching ExtRef information. + * + * @return A list of SclReport Objects that contain errors + */ + public List updateDoInRef() { + return getDOIAdapters().stream() + .filter(doiAdapter -> doiAdapter.getCurrentElem().isSetName() + && doiAdapter.getCurrentElem().getName().startsWith(INREF_PREFIX) + && doiAdapter.findDataAdapterByName(DAI_NAME_PURPOSE).isPresent()) + .map(doiAdapter -> doiAdapter.getDataAdapterByName(DAI_NAME_PURPOSE).getCurrentElem().getVal().stream() + .findFirst() + .map(tVal -> doiAdapter.updateDaiFromExtRef(getExtRefsByDesc(tVal.getValue()))) + .orElse(Optional.of(SclReportItem.warning(getXPath(), "The DOI %s can't be bound with an ExtRef".formatted(getXPath()))))) + .flatMap(Optional::stream) + .toList(); + } + + private List getExtRefsByDesc(String desc) { + return getExtRefs().stream() + .filter(tExtRef -> tExtRef.isSetDesc() && tExtRef.getDesc().contains(desc)) + .toList(); + } } diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/package-info.java index 5d8086435..5bbacdaba 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/ied/package-info.java @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

    scl.ied is a group of services for operating on + *

    scl.ied is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.TIED IED} object - *

    + * * @see org.lfenergy.compas.scl2007b4.model.TAccessPoint * @see org.lfenergy.compas.scl2007b4.model.TServices * @see org.lfenergy.compas.scl2007b4.model.TLDevice diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/package-info.java index 9385c8a5a..4529100aa 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/package-info.java @@ -3,13 +3,13 @@ // SPDX-License-Identifier: Apache-2.0 /** - *

    commons.scl is a group of services for operating on + *

    commons.scl is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.THeader Header} , * {@link org.lfenergy.compas.scl2007b4.model.TSubstation Substation} , * {@link org.lfenergy.compas.scl2007b4.model.TCommunication Communication} , * {@link org.lfenergy.compas.scl2007b4.model.TIED IED} , * {@link org.lfenergy.compas.scl2007b4.model.TDataTypeTemplates DataTypeTemplates} objects - *

    + * * @see org.lfenergy.compas.scl2007b4.model.SCL */ package org.lfenergy.compas.sct.commons.scl; \ No newline at end of file diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/sstation/package-info.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/sstation/package-info.java index 2f3987594..45b2f2e0d 100644 --- a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/sstation/package-info.java +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/scl/sstation/package-info.java @@ -2,9 +2,9 @@ // // SPDX-License-Identifier: Apache-2.0 /** - *

    scl.sstation is a group of services for operating on + *

    scl.sstation is a group of services for operating on * {@link org.lfenergy.compas.scl2007b4.model.TSubstation Substation} object - *

    + * * @see org.lfenergy.compas.scl2007b4.model.TVoltageLevel * @see org.lfenergy.compas.scl2007b4.model.TFunction * @see org.lfenergy.compas.scl2007b4.model.TBay diff --git a/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java new file mode 100644 index 000000000..cfa6755e0 --- /dev/null +++ b/sct-commons/src/main/java/org/lfenergy/compas/sct/commons/util/ServicesConfigEnum.java @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2022 RTE FRANCE +// +// SPDX-License-Identifier: Apache-2.0 + +package org.lfenergy.compas.sct.commons.util; + +public enum ServicesConfigEnum { + GSE, + SMV, + REPORT, + DATASET, + FCDA; + +} diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java index dc98feb9b..0fd7e179c 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/PrivateServiceTest.java @@ -437,7 +437,7 @@ void createMapIEDNameAndPrivate_should_return_map_of_three_items() { //When SclRootAdapter sclRootAdapter = SclService.initScl(Optional.empty(), "hv", "hr"); sclRootAdapter.setCurrentElem(scl); - Stream tPrivateStream = PrivateService.createMapIEDNameAndPrivate(sclRootAdapter); + Stream tPrivateStream = PrivateService.streamIcdHeaderPrivatesWithDistinctIEDName(sclRootAdapter); //Then assertThat(tPrivateStream.toList()).hasSize(3) @@ -467,7 +467,7 @@ void createMapIEDNameAndPrivate_should_return_empty_map_when_no_compasicdheader_ //When SclRootAdapter sclRootAdapter = SclService.initScl(Optional.empty(), "hv", "hr"); sclRootAdapter.setCurrentElem(scl); - Stream tPrivateStream = PrivateService.createMapIEDNameAndPrivate(sclRootAdapter); + Stream tPrivateStream = PrivateService.streamIcdHeaderPrivatesWithDistinctIEDName(sclRootAdapter); //Then assertThat(tPrivateStream.toList()).isEmpty(); diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclServiceTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclServiceTest.java index 645615ced..94a38dcf4 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclServiceTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/SclServiceTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.MethodSource; import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.*; @@ -133,7 +134,6 @@ void testAddSubnetworksWithoutImportingIcdAddressAndPhysConn() { assertThat(marshalledScd).doesNotContain("
    ", "PhysConn"); } - @Test void testGetSubnetwork() { SclRootAdapter sclRootAdapter = new SclRootAdapter("hId", SclRootAdapter.VERSION, SclRootAdapter.REVISION); @@ -285,7 +285,7 @@ void getExtRefSourceInfo_shouldReturnEmptyList_whenExtRefMatchNoFCDA() { extRefInfo.setHolderLnClass(lnClass); //When - List controlBlocks = SclService.getExtRefSourceInfo(scd, extRefInfo); + List controlBlocks = SclService.getExtRefSourceInfo(scd, extRefInfo); //Then assertThat(controlBlocks).isEmpty(); @@ -357,6 +357,7 @@ void updateExtRefSource_shouldThrowScdException_whenBindingInfoNullOrInvalid() { assertThat(extRefInfo.getBindingInfo()).isNotNull(); assertThatThrownBy(() -> SclService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// binding invalid } + @Test void updateExtRefSource_shouldThrowScdException_whenBindingInternalByIedName() { //Given @@ -434,6 +435,7 @@ void updateExtRefSource_shouldThrowScdException_whenSourceInfoNullOrInvalid() { assertThat(extRefInfo.getSourceInfo()).isNotNull(); assertThatThrownBy(() -> SclService.updateExtRefSource(scd, extRefInfo)).isInstanceOf(ScdException.class);// signal invalid } + @Test void updateExtRefSource_shouldThrowScdException_whenBindingExternalBinding() { //Given @@ -751,7 +753,7 @@ void testImportSTDElementsInSCD() { SCL std = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); - SclRootAdapter expectedScdAdapter = assertDoesNotThrow( () -> SclService.importSTDElementsInSCD( + SclRootAdapter expectedScdAdapter = assertDoesNotThrow(() -> SclService.importSTDElementsInSCD( scdRootAdapter, Set.of(std), DTO.comMap)); assertThat(expectedScdAdapter.getCurrentElem().getIED()).hasSize(1); assertThat(expectedScdAdapter.getCurrentElem().getDataTypeTemplates()).hasNoNullFieldsOrProperties(); @@ -767,13 +769,13 @@ void testImportSTDElementsInSCD_with_Multiple_STD() { SCL std2 = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std_SITESITE1SCU2.xml"); SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); - SclRootAdapter expectedScdAdapter = assertDoesNotThrow( () -> SclService.importSTDElementsInSCD( + SclRootAdapter expectedScdAdapter = assertDoesNotThrow(() -> SclService.importSTDElementsInSCD( scdRootAdapter, Set.of(std0, std1, std2), DTO.comMap)); assertThat(expectedScdAdapter.getCurrentElem().getIED()).hasSize(3); assertThat(expectedScdAdapter.getCurrentElem().getDataTypeTemplates()).hasNoNullFieldsOrProperties(); assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork()).hasSize(2); - assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork().get(0).getConnectedAP()).hasSizeBetween(1,3); - assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork().get(1).getConnectedAP()).hasSizeBetween(1,3); + assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork().get(0).getConnectedAP()).hasSizeBetween(1, 3); + assertThat(expectedScdAdapter.getCurrentElem().getCommunication().getSubNetwork().get(1).getConnectedAP()).hasSizeBetween(1, 3); assertIsMarshallable(scd); } @@ -790,6 +792,17 @@ void testImportSTDElementsInSCD_Several_STD_Match_Compas_ICDHeader() { assertIsMarshallable(scd); } + @Test + void test_importSTDElementsInSCD_should_not_throw_exception_when_SCD_file_contains_same_ICDHeader_in_two_different_functions() { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml"); + SCL std = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); + SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); + //When Then + assertDoesNotThrow(() -> SclService.importSTDElementsInSCD(scdRootAdapter, Set.of(std), DTO.comMap)); + assertIsMarshallable(scd); + } + @Test void testImportSTDElementsInSCD_Compas_ICDHeader_Not_Match() { //Given @@ -798,7 +811,7 @@ void testImportSTDElementsInSCD_Compas_ICDHeader_Not_Match() { SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); //When Then Set stds = Set.of(std); - assertThrows(ScdException.class, ()-> SclService.importSTDElementsInSCD(scdRootAdapter, stds, DTO.comMap)); + assertThrows(ScdException.class, () -> SclService.importSTDElementsInSCD(scdRootAdapter, stds, DTO.comMap)); assertIsMarshallable(scd); } @@ -807,9 +820,9 @@ void testImportSTDElementsInSCD_No_STD_Match() { //Given SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/ssd.xml"); SclRootAdapter scdRootAdapter = new SclRootAdapter(scd); - //When Then + //When Then Set stds = new HashSet<>(); - assertThrows(ScdException.class, ()-> SclService.importSTDElementsInSCD(scdRootAdapter, stds, DTO.comMap)); + assertThrows(ScdException.class, () -> SclService.importSTDElementsInSCD(scdRootAdapter, stds, DTO.comMap)); assertIsMarshallable(scd); } @@ -891,10 +904,10 @@ private static Stream sclProviderMissingRequiredObjects() { Tuple[] scl4Errors = new Tuple[]{Tuple.tuple("The LDevice doesn't have a DO @name='Mod'", "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0")}; return Stream.of( - Arguments.of("MissingDOBeh",scl1, scl1Errors), - Arguments.of("MissingLDevicePrivate",scl2, scl2Errors), - Arguments.of("MissingLDevicePrivateAttribute",scl3, scl3Errors), - Arguments.of("MissingDOMod",scl4, scl4Errors) + Arguments.of("MissingDOBeh", scl1, scl1Errors), + Arguments.of("MissingLDevicePrivate", scl2, scl2Errors), + Arguments.of("MissingLDevicePrivateAttribute", scl3, scl3Errors), + Arguments.of("MissingDOMod", scl4, scl4Errors) ); } @@ -923,21 +936,21 @@ private static Stream sclProviderBasedLDeviceStatus() { SCL scl2 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test_LD_STATUS_UNTESTED.scd"); SCL scl3 = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue68_Test1_LD_STATUS_INACTIVE.scd"); Tuple[] scl1Errors = new Tuple[]{Tuple.tuple("The LDevice cannot be set to 'off' but has not been selected into SSD.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" )}; Tuple[] scl2Errors = new Tuple[]{Tuple.tuple("The LDevice cannot be set to 'off' but has not been selected into SSD.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", "/SCL/IED[@name=\"IedName3\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0" )}; Tuple[] scl3Errors = new Tuple[]{Tuple.tuple("The LDevice is not qualified into STD but has been selected into SSD.", - "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), + "/SCL/IED[@name=\"IedName1\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be set to 'on' but has been selected into SSD.", "/SCL/IED[@name=\"IedName2\"]/AccessPoint/Server/LDevice[@inst=\"LDSUIED\"]/LN0"), Tuple.tuple("The LDevice cannot be activated or desactivated because its BehaviourKind Enum contains NOT 'on' AND NOT 'off'.", @@ -1024,21 +1037,173 @@ void updateLDeviceStatus_shouldReturnUpdatedFile() { assertEquals("off", getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName3", "LDSUIED").get().getValue()); } - private Optional getLDeviceStatusValue(SCL scl, String iedName, String ldInst){ + @Test + void updateLDeviceStatus_shouldReturnUpdatedFile_when_DAI_Mod_DO_stVal_whatever_it_is_updatable_or_not() { + // Given + SCL givenScl = SclTestMarshaller.getSCLFromFile("/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd"); + assertThat(getLDeviceStatusValue(givenScl, "IedName1", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + assertThat(getLDeviceStatusValue(givenScl, "IedName2", "LDSUIED")) + .map(TVal::getValue) + .hasValue("on"); + assertThat(getLDeviceStatusValue(givenScl, "IedName3", "LDSUIED")) + .map(TVal::getValue) + .isNotPresent(); + assertThat(getLDeviceStatusValue(givenScl, "IedName4", "LDSUIED")) + .map(TVal::getValue) + .hasValue("on"); + assertThat(getLDeviceStatusValue(givenScl, "IedName5", "LDSUIED")) + .map(TVal::getValue) + .hasValue("on"); + + // When + SclReport sclReport = SclService.updateLDeviceStatus(givenScl); + + // Then + assertThat(sclReport.isSuccess()).isTrue(); + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName1", "LDSUIED")) + .map(TVal::getValue) + .hasValue("on"); + + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName2", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName3", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName4", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + + assertThat(getLDeviceStatusValue(sclReport.getSclRootAdapter().getCurrentElem(), "IedName5", "LDSUIED")) + .map(TVal::getValue) + .hasValue("off"); + } + + private Optional getLDeviceStatusValue(SCL scl, String iedName, String ldInst) { + return getValFromDaiName(scl, iedName, ldInst, "Mod", "stVal"); + } + + @ParameterizedTest(name = "{0}") + @CsvSource({ + "Test update setSrcRef Value,LD_WITH_1_InRef,InRef2,setSrcRef,IED_NAME1LD_WITH_1_InRef/PRANCR1.Do11.sdo11", + "Test update setSrcCB Value,LD_WITH_1_InRef,InRef2,setSrcCB,IED_NAME1LD_WITH_1_InRef/prefixANCR1.GSE1", + "Test update setSrcRef Value,LD_WITH_3_InRef,InRef3,setSrcRef,IED_NAME1LD_WITH_3_InRef/PRANCR1.Do11.sdo11", + "Test update setSrcCB Value,LD_WITH_3_InRef,InRef3,setSrcCB,IED_NAME1LD_WITH_3_InRef/prefixANCR1.GSE1", + "Test update setTstRef Value,LD_WITH_3_InRef,InRef3,setTstRef,IED_NAME1LD_WITH_3_InRef/PRANCR1.Do11.sdo11", + "Test update setTstCB Value,LD_WITH_3_InRef,InRef3,setTstCB,IED_NAME1LD_WITH_3_InRef/prefixANCR3.GSE3" + }) + void updateDoInRef_shouldReturnUpdatedFile(String testName, String ldInst, String doName, String daName, String expected) { + // Given + SCL givenScl = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml"); + + // When + SclReport sclReport = SclService.updateDoInRef(givenScl); + + // Then + assertThat(sclReport.isSuccess()).isTrue(); + assertThat(getValFromDaiName(sclReport.getSclRootAdapter().getCurrentElem(), "IED_NAME1", ldInst, doName, daName) + .map(TVal::getValue)) + .hasValue(expected); + } + + @ParameterizedTest(name = "{0}") + @CsvSource({ + "Test with only 1 ExtRef should not update srcTstCB,LD_WITH_1_InRef,InRef2,setTstRef", + "Test with only 1 ExtRef should not update setTstCB Value,LD_WITH_1_InRef,InRef2,setTstCB" + }) + void updateDoInRef_should_not_update_tst_DAI_When_only_1_ExtRef(String testName, String ldInst, String doName, String daName) { + // Given + SCL givenScl = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml"); + + // When + SclReport sclReport = SclService.updateDoInRef(givenScl); + + // Then + assertThat(sclReport.isSuccess()).isTrue(); + assertThat(getValFromDaiName(sclReport.getSclRootAdapter().getCurrentElem(), "IED_NAME1", ldInst, doName, daName)).isNotPresent(); + } + + @Test + void updateDoInRef_shouldReturnReportWithError_when_ExtRef_not_coherent() { + // Given + SCL givenScl = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml"); + + // When + SclReport sclReport = SclService.updateDoInRef(givenScl); + + // Then + assertThat(sclReport.isSuccess()).isTrue(); + assertThat(sclReport.getSclReportItems()).isNotEmpty(); + assertThat(sclReport.getSclReportItems()).hasSize(4); + } + + private Optional getValFromDaiName(SCL scl, String iedName, String ldInst, String doiName, String daiName) { SclRootAdapter sclRootAdapter = new SclRootAdapter(scl); IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName(iedName); Optional lDeviceAdapter = iedAdapter.findLDeviceAdapterByLdInst(ldInst); LN0Adapter ln0Adapter = lDeviceAdapter.get().getLN0Adapter(); Optional doiAdapter = ln0Adapter.getDOIAdapters().stream() - .filter(doiAdapter1 -> doiAdapter1.getCurrentElem().getName().equals("Mod")) + .filter(doiAdapter1 -> doiAdapter1.getCurrentElem().getName().equals(doiName)) .findFirst(); - if(doiAdapter.isEmpty()) return Optional.empty(); - return doiAdapter.get().getCurrentElem().getSDIOrDAI().stream() + return doiAdapter.flatMap(adapter -> adapter.getCurrentElem().getSDIOrDAI().stream() .filter(tUnNaming -> tUnNaming.getClass().equals(TDAI.class)) .map(TDAI.class::cast) - .filter(tdai -> tdai.getName().equals("stVal")) + .filter(tdai -> tdai.getName().equals(daiName) && !tdai.getVal().isEmpty()) .map(tdai -> tdai.getVal().get(0)) - .findFirst(); + .findFirst()); } + @Test + void analyzeDataGroups_should_success() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter1 = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxAttributes(9L); + iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxGOOSE(3L); + iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxSMV(2L); + iedAdapter1.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxReports(1L); + // When + SclReport sclReport = SclService.analyzeDataGroups(scd); + //Then + assertThat(sclReport.getSclReportItems()).isEmpty(); + + } + + @Test + void analyzeDataGroups_should_return_errors_messages() { + + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME2"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfDataSet().setMaxAttributes(1L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfDataSet().setMax(3L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getSMVsc().setMax(1L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getGOOSE().setMax(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfReportControl().setMax(0L); + // When + SclReport sclReport = SclService.analyzeDataGroups(scd); + //Then + assertThat(sclReport.getSclReportItems()).hasSize(11) + .extracting(SclReportItem::getMessage) + .containsExactlyInAnyOrder( + "There are too much FCDA for the Client IED IED_NAME1", + "The Client IED IED_NAME1 subscribes to too much GOOSE Control Blocks.", + "The Client IED IED_NAME1 subscribes to too much REPORT Control Blocks.", + "The Client IED IED_NAME1 subscribes to too much SMV Control Blocks.", + "There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST21 in IED IED_NAME2", + "There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST22 in IED IED_NAME2", + "There are too much FCDA for the DataSet DATASET5 for the LDevice LD_INST22 in IED IED_NAME2", + "There are too much DataSets for the IED IED_NAME2", + "There are too much Report Control Blocks for the IED IED_NAME2", + "There are too much GOOSE Control Blocks for the IED IED_NAME2", + "There are too much SMV Control Blocks for the IED IED_NAME2"); + } + + } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java new file mode 100644 index 000000000..1ecfc9daf --- /dev/null +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/AccessPointAdapterTest.java @@ -0,0 +1,278 @@ +/* + * // SPDX-FileCopyrightText: 2023 RTE FRANCE + * // + * // SPDX-License-Identifier: Apache-2.0 + */ + +package org.lfenergy.compas.sct.commons.scl.ied; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.lfenergy.compas.scl2007b4.model.*; +import org.lfenergy.compas.sct.commons.dto.DTO; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; +import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; +import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; +import org.lfenergy.compas.sct.commons.util.ServicesConfigEnum; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +class AccessPointAdapterTest { + + @Test + void amChildElementRef() { + //Given + SclRootAdapter sclRootAdapter = new SclRootAdapter("hID","hVersion","hRevision"); + TIED tied = new TIED(); + tied.setName(DTO.HOLDER_IED_NAME); + TAccessPoint tAccessPoint = new TAccessPoint(); + tAccessPoint.setName("AP_NAME"); + tAccessPoint.setServices(new TServices()); + tied.getAccessPoint().add(tAccessPoint); + sclRootAdapter.getCurrentElem().getIED().add(tied); + IEDAdapter iAdapter = sclRootAdapter.getIEDAdapterByName(DTO.HOLDER_IED_NAME); + //When + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iAdapter, iAdapter.getCurrentElem().getAccessPoint().get(0)); + //Then + assertThat(accessPointAdapter.amChildElementRef()).isTrue(); + assertThat(accessPointAdapter.getCurrentElem().getServices()).isNotNull(); + } + + @ParameterizedTest + @CsvSource(value = {"AP_NAME;AccessPoint[@name=\"AP_NAME\"]", ";AccessPoint[not(@name)]"} + , delimiter = ';') + void elementXPath(String apName, String message) { + // Given + TAccessPoint tAccessPoint = new TAccessPoint(); + tAccessPoint.setName(apName); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(null, tAccessPoint); + // When + String elementXPathResult = accessPointAdapter.elementXPath(); + // Then + assertThat(elementXPathResult).isEqualTo(message); + } + + @Test + void getXPath() { + // Given + TIED tied = new TIED(); + tied.setName("IED_NAME"); + TAccessPoint tAccessPoint = new TAccessPoint(); + tAccessPoint.setName("AP_NAME"); + tied.getAccessPoint().add(tAccessPoint); + IEDAdapter iedAdapter = new IEDAdapter(null, tied); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, tAccessPoint); + // When + String elementXPathResult = accessPointAdapter.getXPath(); + // Then + assertThat(elementXPathResult).isEqualTo("/IED[@name=\"IED_NAME\"]/AccessPoint[@name=\"AP_NAME\"]"); + } + + @Test + void checkFCDALimitations_should_succed_no_error_message() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + //When + List sclReportItems = accessPointAdapter.checkFCDALimitations(); + //Then + assertThat(sclReportItems).isEmpty(); + } + + @Test + void checkFCDALimitations_should_fail_with_one_error_messages() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + accessPointAdapter.getCurrentElem().getServices().getConfDataSet().setMaxAttributes(2L); + //When + List sclReportItems = accessPointAdapter.checkFCDALimitations(); + //Then + assertThat(sclReportItems).hasSize(1) + .extracting(SclReportItem::getMessage) + .containsExactlyInAnyOrder("There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST21 in IED IED_NAME"); + } + @Test + void checkFCDALimitations_should_fail_with_four_error_messages() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + accessPointAdapter.getCurrentElem().getServices().getConfDataSet().setMaxAttributes(1L); + //When + List sclReportItems = accessPointAdapter.checkFCDALimitations(); + //Then + assertThat(sclReportItems).hasSize(4) + .extracting(SclReportItem::getMessage) + .containsExactlyInAnyOrder("There are too much FCDA for the DataSet DATASET3 for the LDevice LD_INST21 in IED IED_NAME", + "There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST21 in IED IED_NAME", + "There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST22 in IED IED_NAME", + "There are too much FCDA for the DataSet DATASET5 for the LDevice LD_INST22 in IED IED_NAME"); + } + + + @Test + void checkControlsLimitation_should_fail_for_dataset_with_one_error_messages() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + accessPointAdapter.getCurrentElem().getServices().getConfDataSet().setMax(5L); + String message = "Too much DataSet for"; + //When + Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.DATASET,message); + //Then + assertThat(sclReportItem).isPresent() + .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME"); + } + + @Test + void checkControlsLimitation_should_fail_for_smv_with_one_error_messages() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + accessPointAdapter.getCurrentElem().getServices().getSMVsc().setMax(2L); + String message = "Too much SMV Control for"; + //When + Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.SMV,message); + //Then + assertThat(sclReportItem).isPresent() + .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME"); + } + + @Test + void checkControlsLimitation_should_fail_for_goose_with_one_error_messages() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + accessPointAdapter.getCurrentElem().getServices().getGOOSE().setMax(2L); + String message = "Too much Goose Control for"; + //When + Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.GSE,message); + //Then + assertThat(sclReportItem).isPresent() + .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME"); + } + + @Test + void checkControlsLimitation_should_fail_for_report_with_one_error_messages() throws Exception { + //Given + AccessPointAdapter accessPointAdapter = provideAPForCheckLimitationForIED(); + accessPointAdapter.getCurrentElem().getServices().getConfReportControl().setMax(0L); + String message = "Too much Report Control for"; + //When + Optional sclReportItem = accessPointAdapter.checkControlsLimitation(ServicesConfigEnum.REPORT,message); + //Then + assertThat(sclReportItem).isPresent() + .get().extracting(SclReportItem::getMessage).isEqualTo(message +" IED_NAME"); + } + + public static AccessPointAdapter provideAPForCheckLimitationForIED() throws Exception { + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME"); + return new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + } + + @Test + void checkLimitationForBindedIEDFCDAs_should_success_no_error() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxAttributes(11L); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + String message = "Too much FCDA"; + List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs(); + //When + Optional sclReportItem = accessPointAdapter.checkLimitationForBoundIEDFCDAs(tExtRefs, message); + + //Then + assertThat(sclReportItem).isEmpty(); + } + + @Test + void checkLimitationForBindedIEDFCDAs_should_fail_one_error_message() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + accessPointAdapter.getCurrentElem().getServices().getClientServices().setMaxAttributes(4L); + List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs(); + String message = "Too much FCDA for IED_NAME1"; + + //When + Optional sclReportItem = accessPointAdapter.checkLimitationForBoundIEDFCDAs(tExtRefs, message); + + //Then + assertThat(sclReportItem).isPresent() + .get() + .extracting(SclReportItem::getMessage) + .isEqualTo(message); + } + + @Test + void checkLimitationForBindedIEDControls_should_fail_three_error_messages() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs(); + //When + List sclReportItems = accessPointAdapter.checkLimitationForBoundIEDControls(tExtRefs); + + //Then + assertThat(sclReportItems).hasSize(3) + .extracting(SclReportItem::getMessage) + .containsExactlyInAnyOrder("The Client IED IED_NAME1 subscribes to too much GOOSE Control Blocks.", + "The Client IED IED_NAME1 subscribes to too much SMV Control Blocks.", + "The Client IED IED_NAME1 subscribes to too much REPORT Control Blocks."); + } + + @Test + void checkLimitationForBindedIEDControls_should_succed_no_error_message() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxAttributes(11L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxGOOSE(5L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxReports(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxSMV(2L); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + List tExtRefs = accessPointAdapter.getAllCoherentExtRefForAnalyze().tExtRefs(); + //When + List sclReportItems = accessPointAdapter.checkLimitationForBoundIEDControls(tExtRefs); + + //Then + assertThat(sclReportItems).isEmpty(); + } + + @Test + void getAllCoherentExtRefForAnalyze_succed() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + //When + AccessPointAdapter.ExtRefAnalyzeRecord extRefAnalyzeRecord = accessPointAdapter.getAllCoherentExtRefForAnalyze(); + //Then + assertThat(extRefAnalyzeRecord) + .extracting(AccessPointAdapter.ExtRefAnalyzeRecord::sclReportItems) + .asList().isEmpty(); + } + + @Test + void getAllCoherentExtRefForAnalyze_fail_with_one_error() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iedAdapter = sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + AccessPointAdapter accessPointAdapter = new AccessPointAdapter(iedAdapter, iedAdapter.getCurrentElem().getAccessPoint().get(0)); + //When + AccessPointAdapter.ExtRefAnalyzeRecord extRefAnalyzeRecord = accessPointAdapter.getAllCoherentExtRefForAnalyze(); + //Then + assertThat(extRefAnalyzeRecord) + .extracting(AccessPointAdapter.ExtRefAnalyzeRecord::sclReportItems) + .asList().hasSize(1); + } +} \ No newline at end of file diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java index 7873ff9a9..2058b81c5 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/DOIAdapterTest.java @@ -9,15 +9,14 @@ import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.DaTypeName; import org.lfenergy.compas.sct.commons.dto.DoTypeName; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; class DOIAdapterTest { @@ -25,137 +24,154 @@ class DOIAdapterTest { @Test void testConstructor() { LN0 ln0 = new LN0(); - LN0Adapter ln0Adapter = new LN0Adapter(null,ln0); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); TDOI tdoi = new TDOI(); tdoi.setName("Do"); ln0.getDOI().add(tdoi); // test amChildElement - DOIAdapter doiAdapter = assertDoesNotThrow(() -> new DOIAdapter(ln0Adapter,tdoi)); + DOIAdapter doiAdapter = assertDoesNotThrow(() -> new DOIAdapter(ln0Adapter, tdoi)); // test tree map TSDI tsdi = new TSDI(); tsdi.setName("sdo2"); tdoi.getSDIOrDAI().add(tsdi); - assertDoesNotThrow(() -> doiAdapter.getStructuredDataAdapterByName("sdo2")); - assertThrows(ScdException.class, () -> doiAdapter.getStructuredDataAdapterByName("sdo3")); + assertThatCode(() -> doiAdapter.getStructuredDataAdapterByName("sdo2")).doesNotThrowAnyException(); + assertThatThrownBy(() -> doiAdapter.getStructuredDataAdapterByName("sdo3")).isInstanceOf(ScdException.class); TDAI tdai = new TDAI(); tdai.setName("angRef"); tdoi.getSDIOrDAI().add(tdai); - assertDoesNotThrow(() -> doiAdapter.getDataAdapterByName("angRef")); - assertThrows(ScdException.class, () -> doiAdapter.getStructuredDataAdapterByName("bda")); - assertThrows(ScdException.class, () -> doiAdapter.getDataAdapterByName("bda")); + assertThatCode(() -> doiAdapter.getDataAdapterByName("angRef")).doesNotThrowAnyException(); + assertThatThrownBy(() -> doiAdapter.getStructuredDataAdapterByName("bda")).isInstanceOf(ScdException.class); + assertThatThrownBy(() -> doiAdapter.getDataAdapterByName("bda")).isInstanceOf(ScdException.class); } - @Test - void testInnerDAIAdapter(){ + void testInnerDAIAdapter() { + // Given final String TOTO = "toto"; - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","angRef"); - assertNull(daiAdapter.isValImport()); + // When + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "angRef"); + + // Then + assertThat(daiAdapter.getCurrentElem().isSetValImport()).isFalse(); daiAdapter.setValImport(true); - assertTrue(daiAdapter.isValImport()); + assertThat(daiAdapter.getCurrentElem().isSetValImport()).isTrue(); // test tree map - assertThrows(UnsupportedOperationException.class, () -> daiAdapter.getDataAdapterByName(TOTO)); - assertThrows( - UnsupportedOperationException.class, - () -> daiAdapter.getStructuredDataAdapterByName(TOTO) - ); + assertThatThrownBy(() -> daiAdapter.getDataAdapterByName(TOTO)).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(() -> daiAdapter.getStructuredDataAdapterByName(TOTO)).isInstanceOf(UnsupportedOperationException.class); - assertThrows(UnsupportedOperationException.class, () -> daiAdapter.addDAI(TOTO)); - assertThrows(UnsupportedOperationException.class, () -> daiAdapter.addSDOI(TOTO)); + assertThatThrownBy(() -> daiAdapter.addDAI(TOTO)).isInstanceOf(UnsupportedOperationException.class); + assertThatThrownBy(() -> daiAdapter.addSDOI(TOTO)).isInstanceOf(UnsupportedOperationException.class); } @Test - void testInnerDAIAdapterTestUpdateWithMapAsArg(){ + void testInnerDAIAdapterTestUpdateWithMapAsArg() { + // Given final String TOTO = "toto"; - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","da"); + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); daiAdapter.setValImport(true); // update DAI val - final Map vals = Collections.singletonMap(0L,TOTO); - assertDoesNotThrow(() -> daiAdapter.update(vals)); - assertFalse(daiAdapter.getCurrentElem().getVal().isEmpty()); + final Map vals = Collections.singletonMap(0L, TOTO); + assertThatCode(() -> daiAdapter.update(vals)).doesNotThrowAnyException(); + assertThat(daiAdapter.getCurrentElem().getVal()).isNotEmpty(); TVal tVal = daiAdapter.getCurrentElem().getVal().get(0); - assertFalse(tVal.isSetSGroup()); + assertThat(tVal.isSetSGroup()).isFalse(); + + final Map vals2 = new HashMap<>(); + vals2.put(1L, TOTO); + vals2.put(0L, TOTO); - final Map vals2 = new HashMap<>(); - vals2.put(1L,TOTO); - vals2.put(0L,TOTO); - assertDoesNotThrow(() -> daiAdapter.update(vals2)); - assertFalse(daiAdapter.getCurrentElem().getVal().isEmpty()); + // When Then + assertThatCode(() -> daiAdapter.update(vals2)).doesNotThrowAnyException(); + assertThat(daiAdapter.getCurrentElem().getVal()).isNotEmpty(); tVal = daiAdapter.getCurrentElem().getVal().get(0); - assertFalse(tVal.isSetSGroup()); + assertThat(tVal.isSetSGroup()).isFalse(); } @Test - void testInnerDAIAdapterTestUpdate(){ + void testInnerDAIAdapterTestUpdate() { + // Given final String TOTO = "toto"; - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","da"); + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); daiAdapter.setValImport(false); - assertThrows(ScdException.class,() -> daiAdapter.update(0L,TOTO) ); + assertThatThrownBy(() -> daiAdapter.update(0L, TOTO)).isInstanceOf(ScdException.class); daiAdapter.setValImport(true); - assertDoesNotThrow(() -> daiAdapter.update(0L,TOTO)); + assertThatCode(() -> daiAdapter.update(0L, TOTO)).doesNotThrowAnyException(); + + final Map vals2 = new HashMap<>(); + vals2.put(1L, TOTO); + vals2.put(2L, TOTO); - final Map vals2 = new HashMap<>(); - vals2.put(1L,TOTO); - vals2.put(2L,TOTO); - assertDoesNotThrow(() -> daiAdapter.update(vals2)); - vals2.put(2L,TOTO + "1"); - assertDoesNotThrow(() -> daiAdapter.update(vals2)); + // When Then + assertThatCode(() -> daiAdapter.update(vals2)).doesNotThrowAnyException(); + vals2.put(2L, TOTO + "1"); + assertThatCode(() -> daiAdapter.update(vals2)).doesNotThrowAnyException(); } @Test void testFindDeepestMatch() throws Exception { + // Given SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); - DOIAdapter doiAdapter = assertDoesNotThrow(()-> ln0Adapter.getDOIAdapterByName("Do")); + DOIAdapter doiAdapter = assertDoesNotThrow(() -> ln0Adapter.getDOIAdapterByName("Do")); DoTypeName doTypeName = new DoTypeName("Do.sdo1.d"); DaTypeName daTypeName = new DaTypeName("antRef.bda1.bda2.bda3"); - Pair pair = doiAdapter.findDeepestMatch( - doTypeName.getStructNames(),0,false + Pair pair = doiAdapter.findDeepestMatch( + doTypeName.getStructNames(), 0, false ); SDIAdapter lastSDOIAdapter = (SDIAdapter) pair.getLeft(); - assertEquals(1,pair.getRight()); - assertNotNull(lastSDOIAdapter); - assertEquals(SDIAdapter.class,lastSDOIAdapter.getClass()); + assertThat(pair.getRight()).isEqualTo(1); + assertThat(lastSDOIAdapter) + .isNotNull() + .isInstanceOf(SDIAdapter.class); SDIAdapter firstDAIAdapter = lastSDOIAdapter.getStructuredDataAdapterByName(daTypeName.getName()); + + // When pair = firstDAIAdapter.findDeepestMatch( - daTypeName.getStructNames(),0,true + daTypeName.getStructNames(), 0, true ); - assertEquals(2,pair.getRight()); - assertNotNull(pair.getLeft()); - assertEquals(SDIAdapter.DAIAdapter.class,pair.getLeft().getClass()); + + // Then + assertThat(pair.getRight()).isEqualTo(2); + assertThat(pair.getLeft()).isNotNull(); + assertThat(pair.getLeft()).isInstanceOf(SDIAdapter.DAIAdapter.class); } - private DOIAdapter.DAIAdapter initInnerDAIAdapter(String doName, String daName){ + private DOIAdapter.DAIAdapter initInnerDAIAdapter(String doName, String daName) { TDOI tdoi = new TDOI(); tdoi.setName(doName); - DOIAdapter doiAdapter = new DOIAdapter(null,tdoi); + DOIAdapter doiAdapter = new DOIAdapter(null, tdoi); TDAI tdai = new TDAI(); tdai.setName(daName); tdoi.getSDIOrDAI().add(tdai); - DOIAdapter.DAIAdapter daiAdapter = assertDoesNotThrow(() -> new DOIAdapter.DAIAdapter(doiAdapter,tdai)); + DOIAdapter.DAIAdapter daiAdapter = assertDoesNotThrow(() -> new DOIAdapter.DAIAdapter(doiAdapter, tdai)); return daiAdapter; } @Test void addPrivate() { - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","da"); + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); TPrivate tPrivate = new TPrivate(); tPrivate.setType("Private Type"); tPrivate.setSource("Private Source"); - assertTrue(daiAdapter.getCurrentElem().getPrivate().isEmpty()); + assertThat(daiAdapter.getCurrentElem().getPrivate()).isEmpty(); + + // When daiAdapter.addPrivate(tPrivate); - assertEquals(1, daiAdapter.getCurrentElem().getPrivate().size()); + + // Then + assertThat(daiAdapter.getCurrentElem().getPrivate()).hasSize(1); } @Test @@ -163,8 +179,8 @@ void elementXPath_doi() { // Given TDOI tdoi = new TDOI(); tdoi.setName("doName"); - DOIAdapter doiAdapter = new DOIAdapter(null,new TDOI()); - DOIAdapter namedDoiAdapter = new DOIAdapter(null,tdoi); + DOIAdapter doiAdapter = new DOIAdapter(null, new TDOI()); + DOIAdapter namedDoiAdapter = new DOIAdapter(null, tdoi); // When String elementXPathResult = doiAdapter.elementXPath(); String namedElementXPathResult = namedDoiAdapter.elementXPath(); @@ -176,11 +192,311 @@ void elementXPath_doi() { @Test void elementXPath_dai() { // Given - DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do","da"); + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); // When String result = daiAdapter.elementXPath(); // Then assertThat(result).isEqualTo("DAI[@name=\"da\"]"); } + + @Test + void findDataAdapterByName_should_return_DAIAdapter_when_DA_name_exist() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + + // When + Optional result = doiAdapter.findDataAdapterByName("da"); + + // Then + assertThat(result) + .isPresent() + .map(daiAdapter1 -> daiAdapter1.getCurrentElem().getName()) + .isEqualTo(Optional.of("da")); + } + + @Test + void findDataAdapterByName_should_return_DAIAdapter_when_DA_name_dont_exist() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + + // When + Optional result = doiAdapter.findDataAdapterByName("wrong"); + + // Then + assertThat(result).isEmpty(); + } + + @Test + void updateDaiFromExtRef_should_update_setSrcXX_values_when_ExtRef_desc_suffix_ends_with_1() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + + TExtRef extRef1 = givenExtRef(1, true); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/SRC_PREFIX_1LLN0SRC_LN_INST_1.CB_NAME_1"); + } + + private static Optional getDaiValOfDoi(DOIAdapter doiAdapter, String daName) { + return doiAdapter.getDataAdapterByName(daName).getCurrentElem().getVal().stream().findFirst(); + } + + @Test + void updateDaiFromExtRef_should_update_setSrcRef_value_but_not_setSrcCB_when_ExtRef_dont_contains_CB() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + + TExtRef extRef1 = givenExtRef(1, false); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + assertThatThrownBy(() -> getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB)).isInstanceOf(ScdException.class); + } + + @Test + void updateDaiFromExtRef_should_update_setSrcXX_and_setTstXX_values_when_ExtRef_desc_suffix_ends_with_1_and_3() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + TDAI daiTstRef = new TDAI(); + daiTstRef.setName(DOIAdapter.DA_NAME_SET_TST_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstRef); + TDAI daiTstCb = new TDAI(); + daiTstCb.setName(DOIAdapter.DA_NAME_SET_TST_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstCb); + + TExtRef extRef1 = givenExtRef(1, true); + TExtRef extRef3 = givenExtRef(3, true); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1, extRef3)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/SRC_PREFIX_1LLN0SRC_LN_INST_1.CB_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF).get().getValue()) + .isEqualTo("IED_NAME_3LD_INST_3/PREFIX_3ANCR3.DO_NAME_3"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_CB).get().getValue()) + .isEqualTo("IED_NAME_3LD_INST_3/SRC_PREFIX_3LLN0SRC_LN_INST_3.CB_NAME_3"); + } + + private static TExtRef givenExtRef(int num, boolean withCbName) { + TExtRef extRef1 = new TExtRef(); + extRef1.setIedName("IED_NAME_" + num); + extRef1.setDesc("ExtRef_desc_" + num); + extRef1.setLdInst("LD_INST_" + num); + extRef1.setSrcPrefix("SRC_PREFIX_" + num); + extRef1.setSrcLNInst("SRC_LN_INST_" + num); + extRef1.getLnClass().add("ANCR"); + extRef1.setLnInst(Integer.toString(num)); + extRef1.setPrefix("PREFIX_" + num); + extRef1.setDoName("DO_NAME_" + num); + if (withCbName) { + extRef1.setSrcCBName("CB_NAME_" + num); + } + return extRef1; + } + + @Test + void updateDaiFromExtRef_should_update_only_setSrcRef_and_setTstRef_values_when_ExtRef_desc_suffix_ends_with_1_and_3_without_CB() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + TDAI daiTstRef = new TDAI(); + daiTstRef.setName(DOIAdapter.DA_NAME_SET_TST_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstRef); + TDAI daiTstCb = new TDAI(); + daiTstCb.setName(DOIAdapter.DA_NAME_SET_TST_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstCb); + + TExtRef extRef1 = givenExtRef(1, false); + TExtRef extRef3 = givenExtRef(3, false); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1, extRef3)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB)) + .isNotPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF).get().getValue()) + .isEqualTo("IED_NAME_3LD_INST_3/PREFIX_3ANCR3.DO_NAME_3"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_CB)) + .isNotPresent(); + } + + @Test + void updateDaiFromExtRef_should_return_warning_report_when_none_ExtRef_endin_with_1() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + + TExtRef extRef3 = givenExtRef(3, false); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef3)); + + // Then + assertThat(sclReportItems) + .isPresent() + .isNotEmpty(); + assertThat(sclReportItems.get().getMessage()) + .contains("can't be bound with an ExtRef"); + assertThat(doiAdapter.getDataAdapterByName(DOIAdapter.DA_NAME_SET_SRC_REF)).isNotNull(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)).isNotPresent(); + } + + @Test + void updateDaiFromExtRef_should_create_DAI_when_no_DAI_name_setSrcRef() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + + TExtRef extRef1 = givenExtRef(1, false); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(doiAdapter.getDataAdapterByName(DOIAdapter.DA_NAME_SET_SRC_REF)).isNotNull(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)) + .isPresent() + .map(TVal::getValue) + .contains("IED_NAME_1LD_INST_1/PREFIX_1ANCR1.DO_NAME_1"); + } + + @Test + void updateDaiFromExtRef_should_return_filled_ReportItem_when_no_ExtRef_in_LNode() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of()); + + // Then + assertThat(sclReportItems) + .isPresent() + .isNotEmpty(); + assertThat(sclReportItems.get().getMessage()).contains("can't be bound with an ExtRef"); + } + + @Test + void updateDaiFromExtRef_should_compose_correct_name_when_optional_ExtRef_attributes_are_missing() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + TDAI daiTstRef = new TDAI(); + daiTstRef.setName(DOIAdapter.DA_NAME_SET_TST_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstRef); + TDAI daiTstCb = new TDAI(); + daiTstCb.setName(DOIAdapter.DA_NAME_SET_TST_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiTstCb); + + TExtRef extRef1 = new TExtRef(); + extRef1.setDesc("ExtRef_desc_1"); + extRef1.setIedName("IED_NAME_1"); + extRef1.setLdInst("LD_INST_1"); + extRef1.getLnClass().add("LN_CLASS_1"); + extRef1.setDoName("DO_NAME_1"); + TExtRef extRef3 = new TExtRef(); + extRef3.setDesc("ExtRef_desc_3"); + extRef3.setIedName("IED_NAME_3"); + extRef3.setLdInst("LD_INST_3"); + extRef3.getLnClass().add("LN_CLASS_3"); + extRef3.setDoName("DO_NAME_3"); + + // When + Optional sclReportItems = doiAdapter.updateDaiFromExtRef(List.of(extRef1, extRef3)); + + // Then + assertThat(sclReportItems).isEmpty(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_REF).get().getValue()) + .isEqualTo("IED_NAME_1LD_INST_1/LN_CLASS_1.DO_NAME_1"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_SRC_CB)) + .isNotPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF)).isPresent(); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_REF).get().getValue()) + .isEqualTo("IED_NAME_3LD_INST_3/LN_CLASS_3.DO_NAME_3"); + assertThat(getDaiValOfDoi(doiAdapter, DOIAdapter.DA_NAME_SET_TST_CB)) + .isNotPresent(); + } + + @Test + void updateDaiFromExtRef_should_throw_exception_when_ExtRef_desc_dont_end_with__1() { + // Given + DOIAdapter.DAIAdapter daiAdapter = initInnerDAIAdapter("Do", "da"); + DOIAdapter doiAdapter = daiAdapter.getParentAdapter(); + TDAI daiSrcRef = new TDAI(); + daiSrcRef.setName(DOIAdapter.DA_NAME_SET_SRC_REF); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcRef); + TDAI daiSrcCb = new TDAI(); + daiSrcCb.setName(DOIAdapter.DA_NAME_SET_SRC_CB); + doiAdapter.getCurrentElem().getSDIOrDAI().add(daiSrcCb); + + TExtRef extRef1 = new TExtRef(); + extRef1.setDesc("ExtRefDesc"); + List extRefList = List.of(extRef1); + + // When Then + assertThatThrownBy(() -> doiAdapter.updateDaiFromExtRef(extRefList)) + .isInstanceOf(NumberFormatException.class); + } } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapterTest.java index 635d37bf1..88f867311 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/IEDAdapterTest.java @@ -5,12 +5,10 @@ package org.lfenergy.compas.sct.commons.scl.ied; import org.junit.jupiter.api.Test; -import org.lfenergy.compas.scl2007b4.model.SCL; -import org.lfenergy.compas.scl2007b4.model.TIED; -import org.lfenergy.compas.scl2007b4.model.TPrivate; -import org.lfenergy.compas.scl2007b4.model.TServices; +import org.lfenergy.compas.scl2007b4.model.*; import org.lfenergy.compas.sct.commons.dto.DTO; import org.lfenergy.compas.sct.commons.dto.ExtRefSignalInfo; +import org.lfenergy.compas.sct.commons.dto.SclReportItem; import org.lfenergy.compas.sct.commons.exception.ScdException; import org.lfenergy.compas.sct.commons.scl.ObjectReference; import org.lfenergy.compas.sct.commons.scl.SclRootAdapter; @@ -26,6 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; class IEDAdapterTest { @@ -174,8 +173,8 @@ void testMatches() { @Test void addPrivate() { - SclRootAdapter sclRootAdapter = Mockito.mock(SclRootAdapter.class); - SCL scl = Mockito.mock(SCL.class); + SclRootAdapter sclRootAdapter = mock(SclRootAdapter.class); + SCL scl = mock(SCL.class); Mockito.when(sclRootAdapter.getCurrentElem()).thenReturn(scl); TIED tied = new TIED(); Mockito.when(scl.getIED()).thenReturn(List.of(tied)); @@ -191,8 +190,8 @@ void addPrivate() { @Test void elementXPath() { // Given - SclRootAdapter sclRootAdapter = Mockito.mock(SclRootAdapter.class); - SCL scl = Mockito.mock(SCL.class); + SclRootAdapter sclRootAdapter = mock(SclRootAdapter.class); + SCL scl = mock(SCL.class); Mockito.when(sclRootAdapter.getCurrentElem()).thenReturn(scl); TIED tied = new TIED(); tied.setName("iedName"); @@ -204,4 +203,77 @@ void elementXPath() { assertThat(result).isEqualTo("IED[@name=\"iedName\"]"); } + @Test + void checkDataGroupCoherence_should_succed_no_error_message() throws Exception { + //Given + IEDAdapter iedAdapter = provideIEDForCheckLimitationForIED(); + //When + List sclReportItems = iedAdapter.checkDataGroupCoherence(); + //Then + assertThat(sclReportItems).isEmpty(); + } + + @Test + void checkDataGroupCoherence_should_fail_five_error_message() throws Exception { + //Given + IEDAdapter iedAdapter = provideIEDForCheckLimitationForIED(); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfDataSet().setMaxAttributes(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfDataSet().setMax(5L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getSMVsc().setMax(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getGOOSE().setMax(2L); + iedAdapter.getCurrentElem().getAccessPoint().get(0).getServices().getConfReportControl().setMax(0L); + //When + List sclReportItems = iedAdapter.checkDataGroupCoherence(); + //Then + assertThat(sclReportItems).hasSize(5) + .extracting(SclReportItem::getMessage) + .containsExactlyInAnyOrder("There are too much FCDA for the DataSet DATASET6 for the LDevice LD_INST21 in IED IED_NAME", + "There are too much DataSets for the IED IED_NAME", + "There are too much Report Control Blocks for the IED IED_NAME", + "There are too much GOOSE Control Blocks for the IED IED_NAME", + "There are too much SMV Control Blocks for the IED IED_NAME"); + } + + @Test + void checkBindingDataGroupCoherence_should_succed_no_error_message() throws Exception { + //Given + IEDAdapter iedAdapter = provideIEDForCheckLimitationForBindedIED(); + TClientServices tClientServices = iedAdapter.getParentAdapter().getIEDAdapterByName("IED_NAME1").getCurrentElem().getAccessPoint().get(0).getServices().getClientServices(); + tClientServices.setMaxAttributes(11L); + tClientServices.setMaxGOOSE(5L); + tClientServices.setMaxReports(2L); + tClientServices.setMaxSMV(2L); + //When + List sclReportItems = iedAdapter.checkBindingDataGroupCoherence(); + //Then + assertThat(sclReportItems).isEmpty(); + } + + @Test + void checkBindingDataGroupCoherence_should_fail_five_error_message() throws Exception { + //Given + IEDAdapter iedAdapter = provideIEDForCheckLimitationForBindedIED(); + //When + List sclReportItems = iedAdapter.checkBindingDataGroupCoherence(); + //Then + assertThat(sclReportItems).hasSize(4) + .extracting(SclReportItem::getMessage) + .containsExactlyInAnyOrder("There are too much FCDA for the Client IED IED_NAME1", + "The Client IED IED_NAME1 subscribes to too much SMV Control Blocks.", + "The Client IED IED_NAME1 subscribes to too much REPORT Control Blocks.", + "The Client IED IED_NAME1 subscribes to too much GOOSE Control Blocks."); + + } + + public static IEDAdapter provideIEDForCheckLimitationForBindedIED() throws Exception { + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + return sclRootAdapter.getIEDAdapterByName("IED_NAME1"); + } + + public static IEDAdapter provideIEDForCheckLimitationForIED() throws Exception { + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + return sclRootAdapter.getIEDAdapterByName("IED_NAME"); + } } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterTest.java index 64be79b07..a23706f4d 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/InputsAdapterTest.java @@ -5,6 +5,7 @@ package org.lfenergy.compas.sct.commons.scl.ied; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -14,6 +15,7 @@ import org.lfenergy.compas.sct.commons.testhelpers.FCDARecord; import org.lfenergy.compas.sct.commons.testhelpers.MarshallerWrapper; import org.lfenergy.compas.sct.commons.testhelpers.SclTestMarshaller; +import org.mockito.junit.jupiter.MockitoExtension; import org.opentest4j.AssertionFailedError; import java.util.List; @@ -25,6 +27,7 @@ import static org.junit.jupiter.api.Named.named; import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.*; +@ExtendWith(MockitoExtension.class) class InputsAdapterTest { @Test @@ -255,5 +258,30 @@ private static InputsAdapter keepOnlyThisExtRef(SclRootAdapter sclRootAdapter, S foundInputsAdapter.getCurrentElem().getExtRef().removeIf(Predicate.not(extref -> extRefDesc.equals(extref.getDesc()))); return foundInputsAdapter; } + /* @Test + void checkSourceDataGroupCoherence_should_fail_one_error_messages() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + InputsAdapter inputsAdapter = keepOnlyThisExtRef(sclRootAdapter, "a"); + //When + List sclReportItems = inputsAdapter.checkSourceDataGroupCoherence(); + //Then + assertThat(sclReportItems).hasSize(1) + .extracting(SclReportItem::getMessage) + .containsExactlyInAnyOrder("The Client IED IED_NAME1 subscribes to much GOOSE Control Blocks."); + } + @Test + void checkSourceDataGroupCoherence_should_succed_no_error_message() throws Exception { + //Given + SCL scd = SclTestMarshaller.getSCLFromFile("/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + InputsAdapter inputsAdapter = keepOnlyThisExtRef(sclRootAdapter, "a"); + sclRootAdapter.getIEDAdapterByName("IED_NAME1").getCurrentElem().getAccessPoint().get(0).getServices().getClientServices().setMaxGOOSE(1L); + //When + List sclReportItems = inputsAdapter.checkSourceDataGroupCoherence(); + //Then + assertThat(sclReportItems).isEmpty(); + }*/ } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LN0AdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LN0AdapterTest.java index 84b141887..e48d8d48e 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LN0AdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LN0AdapterTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; import org.lfenergy.compas.scl2007b4.model.*; @@ -29,6 +30,7 @@ import static org.junit.jupiter.api.Named.named; import static org.lfenergy.compas.scl2007b4.model.TSampledValueControl.SmvOpts; import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.findLn0; +import static org.lfenergy.compas.sct.commons.testhelpers.SclHelper.getDaiValue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -46,11 +48,11 @@ void testAmChildElementRef() throws ScdException { LN0 ln0 = new LN0(); ln0.setLnType("LT1"); when(tlDevice.getLN0()).thenReturn(ln0); - LN0Adapter ln0Adapter = assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + LN0Adapter ln0Adapter = assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); - assertEquals(LN0.class,ln0Adapter.getElementClassType()); - assertEquals("LT1",ln0Adapter.getLnType()); - assertEquals(TLLN0Enum.LLN_0.value(),ln0Adapter.getLNClass()); + assertEquals(LN0.class, ln0Adapter.getElementClassType()); + assertEquals("LT1", ln0Adapter.getLnType()); + assertEquals(TLLN0Enum.LLN_0.value(), ln0Adapter.getLNClass()); assertFalse(ln0Adapter.hasInputs()); ln0.setInputs(new TInputs()); assertTrue(ln0Adapter.hasInputs()); @@ -63,7 +65,7 @@ void testAmChildElementRef() throws ScdException { assertTrue(ln0Adapter.getCurrentElem().getReportControl().isEmpty()); LN0 ln01 = new LN0(); - assertThrows(IllegalArgumentException.class, () -> new LN0Adapter(lDeviceAdapter, ln01)); + assertThrows(IllegalArgumentException.class, () -> new LN0Adapter(lDeviceAdapter, ln01)); } // AbstractLNAdapter class test @@ -74,8 +76,9 @@ void containsFCDA() { when(lDeviceAdapter.getCurrentElem()).thenReturn(tlDevice); LN0 ln0 = new LN0(); when(tlDevice.getLN0()).thenReturn(ln0); - assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); } + @Test void isExtRefExist_shouldThrowScdException_whenNoInputsInLN0() { //Given @@ -114,7 +117,7 @@ void isExtRefExist_shouldThrowScdException_whenSignalNotValid() { @Test - void isExtRefExist_shouldThrowScdException_whenSignalNull(){ + void isExtRefExist_shouldThrowScdException_whenSignalNull() { //Given LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); TLDevice tlDevice = mock(TLDevice.class); @@ -125,7 +128,7 @@ void isExtRefExist_shouldThrowScdException_whenSignalNull(){ tInputs.getExtRef().add(extRef); ln0.setInputs(tInputs); when(tlDevice.getLN0()).thenReturn(ln0); - LN0Adapter ln0Adapter = assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + LN0Adapter ln0Adapter = assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); //When Then assertThatThrownBy(() -> ln0Adapter.isExtRefExist(null)) .isInstanceOf(ScdException.class) @@ -133,7 +136,7 @@ void isExtRefExist_shouldThrowScdException_whenSignalNull(){ } @Test - void isExtRefExist_shouldThrowScdException_whenNotExistInTargetLN(){ + void isExtRefExist_shouldThrowScdException_whenNotExistInTargetLN() { //Given LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); TLDevice tlDevice = mock(TLDevice.class); @@ -153,7 +156,7 @@ void isExtRefExist_shouldThrowScdException_whenNotExistInTargetLN(){ .hasMessage("ExtRef signal does not exist in target LN"); } - @Test + @Test void isExtRefExist_shouldNotThrowException_whenExtRefExist() { //Given LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); @@ -195,13 +198,13 @@ void isExtRefExist_shouldNotThrowException_whenExtRefExistWithPDA() { } @Test - void testGetDataSetWith(){ + void testGetDataSetWith() { LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); TLDevice tlDevice = mock(TLDevice.class); when(lDeviceAdapter.getCurrentElem()).thenReturn(tlDevice); LN0 ln0 = new LN0(); when(tlDevice.getLN0()).thenReturn(ln0); - LN0Adapter ln0Adapter = assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + LN0Adapter ln0Adapter = assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); TDataSet tDataSet = new TDataSet(); ln0.getDataSet().add(tDataSet); @@ -223,7 +226,7 @@ void testGetDataSetWith(){ } @Test - void testGetControlBlocks(){ + void testGetControlBlocks() { LDeviceAdapter lDeviceAdapter = mock(LDeviceAdapter.class); IEDAdapter iedAdapter = mock(IEDAdapter.class); TLDevice tlDevice = mock(TLDevice.class); @@ -232,7 +235,7 @@ void testGetControlBlocks(){ when(iedAdapter.getName()).thenReturn("IED_NAME"); LN0 ln0 = new LN0(); when(tlDevice.getLN0()).thenReturn(ln0); - LN0Adapter ln0Adapter = assertDoesNotThrow( () -> new LN0Adapter(lDeviceAdapter,ln0)); + LN0Adapter ln0Adapter = assertDoesNotThrow(() -> new LN0Adapter(lDeviceAdapter, ln0)); TGSEControl tgseControl = new TGSEControl(); tgseControl.setDatSet("GSE_REF"); TSampledValueControl tSampledValueControl = new TSampledValueControl(); @@ -246,7 +249,7 @@ void testGetControlBlocks(){ TDataSet tDataSetGSE = new TDataSet(); tDataSetGSE.setName(DTO.CB_DATASET_REF); - List controlBlocks = ln0Adapter.getControlBlocks(List.of(tDataSetGSE),null); + List controlBlocks = ln0Adapter.getControlBlocks(List.of(tDataSetGSE), null); assertTrue(controlBlocks.isEmpty()); tDataSetGSE.setName("GSE_REF"); @@ -257,18 +260,18 @@ void testGetControlBlocks(){ List tDataSets = List.of(tDataSetGSE, tDataSetSMV, tDataSetRPT); - controlBlocks = ln0Adapter.getControlBlocks(tDataSets,TServiceType.REPORT); + controlBlocks = ln0Adapter.getControlBlocks(tDataSets, TServiceType.REPORT); assertThat(controlBlocks).hasSize(1); - controlBlocks = ln0Adapter.getControlBlocks(tDataSets,TServiceType.SMV); + controlBlocks = ln0Adapter.getControlBlocks(tDataSets, TServiceType.SMV); assertThat(controlBlocks).hasSize(1); - controlBlocks = ln0Adapter.getControlBlocks(tDataSets,TServiceType.GOOSE); + controlBlocks = ln0Adapter.getControlBlocks(tDataSets, TServiceType.GOOSE); assertThat(controlBlocks).hasSize(1); - controlBlocks = ln0Adapter.getControlBlocks(tDataSets,null); + controlBlocks = ln0Adapter.getControlBlocks(tDataSets, null); assertThat(controlBlocks).hasSize(3); } @Test - void testGetControlSetByBindingInfo(){ + void testGetControlSetByBindingInfo() { LN0 ln0 = new LN0(); LN0Adapter ln0Adapter = mock(LN0Adapter.class); @@ -287,17 +290,17 @@ void testGetControlSetByBindingInfo(){ Mockito.doReturn(List.of(new ReportControlBlock("rpt", "rptID", "rptDatSet"))) .when(ln0Adapter).getControlBlocks( - any(List.class), any(TServiceType.class)); + any(List.class), any(TServiceType.class)); - List controlBlocks = ln0Adapter.getControlBlocksForMatchingFCDA(extRefBindingInfo); + List controlBlocks = ln0Adapter.getControlBlocksForMatchingFCDA(extRefBindingInfo); assertFalse(controlBlocks.isEmpty()); - assertEquals(TServiceType.REPORT,controlBlocks.get(0).getServiceType()); + assertEquals(TServiceType.REPORT, controlBlocks.get(0).getServiceType()); } @Test - void testGetDOIAdapters(){ + void testGetDOIAdapters() { LN0 ln0 = new LN0(); - LN0Adapter ln0Adapter = new LN0Adapter(null,ln0); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); TDOI tdoi = new TDOI(); tdoi.setName("Do"); @@ -307,7 +310,7 @@ void testGetDOIAdapters(){ } @Test - void testGetDOIAdapterByName(){ + void testGetDOIAdapterByName() { IEDAdapter iedAdapter = mock(IEDAdapter.class); TIED tied = new TIED(); when(iedAdapter.getCurrentElem()).thenReturn(tied); @@ -320,7 +323,7 @@ void testGetDOIAdapterByName(){ LN0 ln0 = new LN0(); tlDevice.setLN0(ln0); - LN0Adapter ln0Adapter = new LN0Adapter(lDeviceAdapter,ln0); + LN0Adapter ln0Adapter = new LN0Adapter(lDeviceAdapter, ln0); TDOI tdoi = new TDOI(); tdoi.setName("Do"); @@ -334,16 +337,16 @@ void testFindMatch() { SCL scd = SclTestMarshaller.getSCLFromFile(SCD_IED_U_TEST); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); DoTypeName doTypeName = new DoTypeName("Do.sdo1.d"); DaTypeName daTypeName = new DaTypeName("antRef.bda1.bda2.bda3"); - AbstractDAIAdapter daiAdapter = (AbstractDAIAdapter) assertDoesNotThrow(() -> ln0Adapter.findMatch(doTypeName,daTypeName).get()); - assertEquals("bda3",daiAdapter.getCurrentElem().getName()); - assertEquals("Completed-diff",daiAdapter.getCurrentElem().getVal().get(0).getValue()); + AbstractDAIAdapter daiAdapter = (AbstractDAIAdapter) assertDoesNotThrow(() -> ln0Adapter.findMatch(doTypeName, daTypeName).get()); + assertEquals("bda3", daiAdapter.getCurrentElem().getName()); + assertEquals("Completed-diff", daiAdapter.getCurrentElem().getVal().get(0).getValue()); DoTypeName doTypeName2 = new DoTypeName("Do.sdo1"); - assertFalse(ln0Adapter.findMatch(doTypeName2,daTypeName).isPresent()); + assertFalse(ln0Adapter.findMatch(doTypeName2, daTypeName).isPresent()); } @ParameterizedTest @@ -399,7 +402,7 @@ void hasControlBlock_when_wrong_controlBlockEnum_should_return_false() { void addPrivate() { LN0 tln = new LN0(); tln.getLnClass().add(TLLN0Enum.LLN_0.value()); - LN0Adapter lnAdapter = new LN0Adapter(null,tln); + LN0Adapter lnAdapter = new LN0Adapter(null, tln); TPrivate tPrivate = new TPrivate(); tPrivate.setType("Private Type"); tPrivate.setSource("Private Source"); @@ -415,7 +418,7 @@ void testGetDAI() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.findLDeviceAdapterByLdInst("LDSUIED").get()); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LDSUIED").get()); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ResumedDataTemplate filter = new ResumedDataTemplate(); filter.setLnClass(ln0Adapter.getLNClass()); @@ -429,10 +432,10 @@ void testGetDAI() { daTypeName.setFc(TFCEnum.ST); filter.setDaName(daTypeName); //When - var rDtts = ln0Adapter.getDAI(filter,false); + var rDtts = ln0Adapter.getDAI(filter, false); //Then assertFalse(rDtts.isEmpty()); - assertEquals(1,rDtts.size()); + assertEquals(1, rDtts.size()); assertNotNull(rDtts.get(0).getDaName().getType()); assertEquals("BehaviourModeKind", rDtts.get(0).getDaName().getType()); } @@ -443,7 +446,7 @@ void getEnumValue_shouldReturnNothing_whenEnumUnknow() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); //When Set enumValues = ln0Adapter.getEnumValues("Behaviour"); @@ -457,7 +460,7 @@ void getEnumValue_shouldReturnEnumValues_whenEnumKnown() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.findLDeviceAdapterByLdInst("LDSUIED").get()); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LDSUIED").get()); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); //When Set enumValues = ln0Adapter.getEnumValues("BehaviourModeKind"); @@ -473,7 +476,7 @@ void addControlBlock_should_add_ControlBlock(ControlBlock controlBlock) { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists(controlBlock.getDataSetRef(), controlBlock.getControlBlockEnum()); int initialControlBlockCount = ln0Adapter.getTControlsByType(controlBlock.getControlBlockEnum().getControlBlockClass()).size(); @@ -489,7 +492,7 @@ void addControlBlock_should_add_ReportControlBlock() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("rptDatSet", ControlBlockEnum.REPORT); ReportControlBlock reportControlBlock = new ReportControlBlock("rpt", "rptID", "rptDatSet"); @@ -497,7 +500,7 @@ void addControlBlock_should_add_ReportControlBlock() { //When ln0Adapter.addControlBlock(reportControlBlock); //Then - assertThat(ln0Adapter.getCurrentElem().getReportControl()).hasSize(reportCBInitSize+1); + assertThat(ln0Adapter.getCurrentElem().getReportControl()).hasSize(reportCBInitSize + 1); } @Test @@ -506,7 +509,7 @@ void addControlBlock_should_add_GooseControlBlock() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("datSet", ControlBlockEnum.GSE); GooseControlBlock gooseControlBlock = new GooseControlBlock("gse", "gseID", "datSet"); @@ -524,7 +527,7 @@ void addControlBlock_should_add_SMVControlBlock() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("smvDatSet", ControlBlockEnum.SAMPLED_VALUE); SMVControlBlock smvControlBlock = new SMVControlBlock("smv", "smvID", "smvDatSet"); @@ -532,7 +535,7 @@ void addControlBlock_should_add_SMVControlBlock() { //When ln0Adapter.addControlBlock(smvControlBlock); //Then - assertThat(ln0Adapter.getCurrentElem().getSampledValueControl()).hasSize(reportCBInitSize+1); + assertThat(ln0Adapter.getCurrentElem().getSampledValueControl()).hasSize(reportCBInitSize + 1); } @ParameterizedTest @@ -542,14 +545,14 @@ void addControlBlock_when_accessPoint_does_not_have_capability_should_throw_exce SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("dataSet", ControlBlockEnum.REPORT); ln0Adapter.getParentLDevice().getAccessPoint().setServices(new TServices()); //When & Then assertThatThrownBy(() -> ln0Adapter.addControlBlock(controlBlock)) - .isInstanceOf(ScdException.class) - .hasMessageContaining("because IED/AccessPoint does not have capability to create ControlBlock"); + .isInstanceOf(ScdException.class) + .hasMessageContaining("because IED/AccessPoint does not have capability to create ControlBlock"); } @ParameterizedTest @@ -559,14 +562,14 @@ void addControlBlock_when_controlBlock_already_exists_should_throw_exception(Con SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); ln0Adapter.createDataSetIfNotExists("dataSet", ControlBlockEnum.REPORT); ln0Adapter.addControlBlock(controlBlock); //When & Then assertThatThrownBy(() -> ln0Adapter.addControlBlock(controlBlock)) - .isInstanceOf(ScdException.class) - .hasMessageContaining("because it already exists"); + .isInstanceOf(ScdException.class) + .hasMessageContaining("because it already exists"); } @ParameterizedTest @@ -576,19 +579,19 @@ void addControlBlock_when_dataSet_does_not_exist_should_throw_exception(ControlB SCL scd = SclTestMarshaller.getSCLFromFile("/scd-ied-dtt-com-import-stds/std.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED4d4fe1a8cda64cf88a5ee4176a1a0eef")); - LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(()-> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.getLDeviceAdapterByLdInst("LDSUIED")); LN0Adapter ln0Adapter = lDeviceAdapter.getLN0Adapter(); //When & Then assertThatThrownBy(() -> ln0Adapter.addControlBlock(controlBlock)) - .isInstanceOf(ScdException.class) - .hasMessageContaining("because target DataSet dataSet does not exists"); + .isInstanceOf(ScdException.class) + .hasMessageContaining("because target DataSet dataSet does not exists"); } - private static Stream provideControlBlocks(){ + private static Stream provideControlBlocks() { return Stream.of( - Arguments.of(named("ReportControlBlock", new ReportControlBlock("name", "id", "dataSet"))), - Arguments.of(named("GooseControlBlock", new GooseControlBlock("name", "id", "dataSet"))), - Arguments.of(named("SMVControlBlock", new SMVControlBlock("name", "id", "dataSet"))) + Arguments.of(named("ReportControlBlock", new ReportControlBlock("name", "id", "dataSet"))), + Arguments.of(named("GooseControlBlock", new GooseControlBlock("name", "id", "dataSet"))), + Arguments.of(named("SMVControlBlock", new SMVControlBlock("name", "id", "dataSet"))) ); } @@ -604,11 +607,11 @@ void getTControlsByType_should_return_LN0_list_of_controls_for_this_class(Class< assertThat(controlList).isSameAs(getter.apply(ln0Adapter.getCurrentElem())); } - private static Stream provideGetTControlsByType(){ + private static Stream provideGetTControlsByType() { return Stream.of( - Arguments.of(TGSEControl.class, (Function>) LN0::getGSEControl), - Arguments.of(TSampledValueControl.class, (Function>) LN0::getSampledValueControl), - Arguments.of(TReportControl.class, (Function>) LN0::getReportControl) + Arguments.of(TGSEControl.class, (Function>) LN0::getGSEControl), + Arguments.of(TSampledValueControl.class, (Function>) LN0::getSampledValueControl), + Arguments.of(TReportControl.class, (Function>) LN0::getReportControl) ); } @@ -617,7 +620,7 @@ void elementXPath() { // Given LN0 tln = new LN0(); tln.getLnClass().add(TLLN0Enum.LLN_0.value()); - LN0Adapter lnAdapter = new LN0Adapter(null,tln); + LN0Adapter lnAdapter = new LN0Adapter(null, tln); // When String result = lnAdapter.elementXPath(); // Then @@ -630,18 +633,18 @@ void getLDeviceStatus_should_succeed() { SCL scd = SclTestMarshaller.getSCLFromFile("/scd-extref-iedname/scd_set_extref_iedname_with_extref_errors.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); Optional optionalLN0Adapter = sclRootAdapter.streamIEDAdapters() - .flatMap(IEDAdapter::streamLDeviceAdapters) - .filter(lDeviceAdapter -> "IED_NAME1LD_INST13".equals(lDeviceAdapter.getLdName())) - .map(LDeviceAdapter::getLN0Adapter) - .findFirst(); + .flatMap(IEDAdapter::streamLDeviceAdapters) + .filter(lDeviceAdapter -> "IED_NAME1LD_INST13".equals(lDeviceAdapter.getLdName())) + .map(LDeviceAdapter::getLN0Adapter) + .findFirst(); assertThat(optionalLN0Adapter).isPresent(); LN0Adapter ln0Adapter = optionalLN0Adapter.get(); // When Optional result = ln0Adapter.getLDeviceStatus(); // Then assertThat(result) - .isPresent() - .hasValue("test"); + .isPresent() + .hasValue("test"); } @Test @@ -656,8 +659,8 @@ void createDataSetIfNotExists_should_create_dataSet() { assertThat(newDataSet.getCurrentElem().getName()).isEqualTo("newDataSet"); assertThat(newDataSet.getParentAdapter().getParentAdapter().getInst()).isEqualTo("LD_INST11"); assertThat(ln0.getCurrentElem().getDataSet()) - .map(TDataSet::getName) - .containsExactly("newDataSet"); + .map(TDataSet::getName) + .containsExactly("newDataSet"); } @Test @@ -670,8 +673,8 @@ void createDataSetIfNotExists_when_dataset_exists_should_not_create_dataset() { DataSetAdapter newDataSet = ln0.createDataSetIfNotExists("existingDataSet", ControlBlockEnum.GSE); // Then assertThat(ln0.getCurrentElem().getDataSet()).hasSize(1) - .map(TDataSet::getName) - .containsExactly("existingDataSet"); + .map(TDataSet::getName) + .containsExactly("existingDataSet"); assertThat(newDataSet.getCurrentElem().getName()).isEqualTo("existingDataSet"); assertThat(newDataSet.getParentAdapter().getParentAdapter().getInst()).isEqualTo("LD_INST12"); } @@ -683,7 +686,7 @@ void createDataSetIfNotExists_when_ied_does_not_have_creation_capabilities_shoul LN0Adapter ln0 = findLn0(new SclRootAdapter(scd), "IED_NAME2", "LD_INST21"); // When & Then assertThatThrownBy(() -> ln0.createDataSetIfNotExists("existingDataSet", ControlBlockEnum.GSE)) - .isInstanceOf(ScdException.class); + .isInstanceOf(ScdException.class); } @Test @@ -700,11 +703,11 @@ void createControlBlockIfNotExists_should_create_GSEControl() { sourceLn0.createControlBlockIfNotExists(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, ControlBlockEnum.GSE); // Then assertThat(sourceLn0.getCurrentElem().getGSEControl()) - .hasSize(1) - .first().extracting(TControl::getName, TGSEControl::getAppID, TControl::getDatSet, - TGSEControl::getType, TGSEControl::isFixedOffs, TGSEControl::getSecurityEnable, TControlWithIEDName::getConfRev) + .hasSize(1) + .first().extracting(TControl::getName, TGSEControl::getAppID, TControl::getDatSet, + TGSEControl::getType, TGSEControl::isFixedOffs, TGSEControl::getSecurityEnable, TControlWithIEDName::getConfRev) .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, - TGSEControlTypeEnum.GOOSE, false, TPredefinedTypeOfSecurityEnum.NONE, 10000L); + TGSEControlTypeEnum.GOOSE, false, TPredefinedTypeOfSecurityEnum.NONE, 10000L); } @Test @@ -721,18 +724,18 @@ void createControlBlockIfNotExists_should_create_SampledValueControl() { sourceLn0.createControlBlockIfNotExists(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, ControlBlockEnum.SAMPLED_VALUE); // Then assertThat(sourceLn0.getCurrentElem().getSampledValueControl()) - .hasSize(1); + .hasSize(1); TSampledValueControl tSampledValueControl = sourceLn0.getCurrentElem().getSampledValueControl().get(0); assertThat(tSampledValueControl) - .extracting(TControl::getName, TSampledValueControl::getSmvID, TControl::getDatSet, - TSampledValueControl::isMulticast, TSampledValueControl::getSmpRate, TSampledValueControl::getNofASDU, TSampledValueControl::getSmpMod, TSampledValueControl::getSecurityEnable, - TControlWithIEDName::getConfRev) - .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, - true, 4800L, 2L, TSmpMod.SMP_PER_SEC, TPredefinedTypeOfSecurityEnum.NONE, 10000L); + .extracting(TControl::getName, TSampledValueControl::getSmvID, TControl::getDatSet, + TSampledValueControl::isMulticast, TSampledValueControl::getSmpRate, TSampledValueControl::getNofASDU, TSampledValueControl::getSmpMod, TSampledValueControl::getSecurityEnable, + TControlWithIEDName::getConfRev) + .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, + true, 4800L, 2L, TSmpMod.SMP_PER_SEC, TPredefinedTypeOfSecurityEnum.NONE, 10000L); assertThat(tSampledValueControl.getSmvOpts()) - .extracting(SmvOpts::isRefreshTime, SmvOpts::isSampleSynchronized, SmvOpts::isSampleRate, SmvOpts::isDataSet, - SmvOpts::isSecurity, SmvOpts::isTimestamp) - .containsExactly(false, true, true, false, false, false); + .extracting(SmvOpts::isRefreshTime, SmvOpts::isSampleSynchronized, SmvOpts::isSampleRate, SmvOpts::isDataSet, + SmvOpts::isSecurity, SmvOpts::isTimestamp) + .containsExactly(false, true, true, false, false, false); } @Test @@ -750,25 +753,250 @@ void createControlBlockIfNotExists_should_create_ReportControl() { sourceLn0.createControlBlockIfNotExists(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, ControlBlockEnum.REPORT); // Then assertThat(sourceLn0.getCurrentElem().getReportControl()) - .hasSize(1); + .hasSize(1); TReportControl tReportControl = sourceLn0.getCurrentElem().getReportControl().get(0); assertThat(tReportControl) - .extracting(TControl::getName, TReportControl::getRptID, TControl::getDatSet, - TReportControl::isBuffered, TReportControl::getBufTime, TReportControl::isIndexed, TControlWithTriggerOpt::getIntgPd, TReportControl::getConfRev) - .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, - true, 0L, true, 60000L, 1L); + .extracting(TControl::getName, TReportControl::getRptID, TControl::getDatSet, + TReportControl::isBuffered, TReportControl::getBufTime, TReportControl::isIndexed, TControlWithTriggerOpt::getIntgPd, TReportControl::getConfRev) + .containsExactly(NEW_CB_NAME, NEW_CB_ID, NEW_DATASET_NAME, + true, 0L, true, 60000L, 1L); assertThat(tReportControl.getTrgOps()) - .extracting(TTrgOps::isDchg, TTrgOps::isQchg, TTrgOps::isPeriod, TTrgOps::isGi) - .containsOnly(true); + .extracting(TTrgOps::isDchg, TTrgOps::isQchg, TTrgOps::isPeriod, TTrgOps::isGi) + .containsOnly(true); assertThat(tReportControl.getTrgOps().isDupd()).isFalse(); assertThat(tReportControl.getOptFields()) - .extracting(TReportControl.OptFields::isSeqNum, TReportControl.OptFields::isTimeStamp, TReportControl.OptFields::isDataSet, - TReportControl.OptFields::isReasonCode, TReportControl.OptFields::isDataRef, TReportControl.OptFields::isEntryID, - TReportControl.OptFields::isConfigRef) - .containsOnly(false); + .extracting(TReportControl.OptFields::isSeqNum, TReportControl.OptFields::isTimeStamp, TReportControl.OptFields::isDataSet, + TReportControl.OptFields::isReasonCode, TReportControl.OptFields::isDataRef, TReportControl.OptFields::isEntryID, + TReportControl.OptFields::isConfigRef) + .containsOnly(false); assertThat(tReportControl.getOptFields().isBufOvfl()).isTrue(); } + @Test + void getFCDAs_should_return_list_of_FCDAs() { + + TFCDA tfcda = new TFCDA(); + tfcda.setFc(TFCEnum.CF); + TFCDA tfcda1 = new TFCDA(); + tfcda1.setFc(TFCEnum.CF); + TFCDA tfcda2 = new TFCDA(); + tfcda2.setFc(TFCEnum.CF); + + TDataSet tDataSet1 = new TDataSet(); + tDataSet1.setName("gse_dat_set"); + tDataSet1.getFCDA().addAll(List.of(tfcda,tfcda1)); + + TDataSet tDataSet2 = new TDataSet(); + tDataSet2.setName("smv_dat_set"); + tDataSet2.getFCDA().add(tfcda2); + + TGSEControl gseCB = new TGSEControl(); + gseCB.setName("gse1"); + gseCB.setDatSet("gse_dat_set"); + TSampledValueControl smvCB = new TSampledValueControl(); + smvCB.setName("smv1"); + smvCB.setDatSet("smv_dat_set"); + + LN0 ln0 = new LN0(); + ln0.getDataSet().addAll(List.of(tDataSet1, tDataSet2)); + ln0.getGSEControl().add(gseCB); + ln0.getSampledValueControl().add(smvCB); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); + + TExtRef tExtRef = new TExtRef(); + tExtRef.setSrcCBName("gse1"); + tExtRef.setServiceType(TServiceType.GOOSE); + + //When + List result = ln0Adapter.getFCDAs(tExtRef); + + //Then + assertThat(result).hasSize(2) + .containsExactlyInAnyOrder(tfcda, tfcda1); + + } + + @Test + void getFCDAs_should_return_empty_list_of_FCDAs() { + + TDataSet tDataSet2 = new TDataSet(); + tDataSet2.setName("smv_dat_set"); + + TSampledValueControl smvCB = new TSampledValueControl(); + smvCB.setName("smv1"); + smvCB.setDatSet("smv_dat_set"); + + LN0 ln0 = new LN0(); + ln0.getDataSet().add(tDataSet2); + ln0.getSampledValueControl().add(smvCB); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); + + TExtRef tExtRef = new TExtRef(); + tExtRef.setSrcCBName("smv1"); + tExtRef.setServiceType(TServiceType.SMV); + + //When + List result = ln0Adapter.getFCDAs(tExtRef); + + //Then + assertThat(result).isEmpty(); + + } + + @Test + void getFCDAs_should_throw_Exception_when_DataSet_not_present() { + + LN0 ln0 = new LN0(); + LN0Adapter ln0Adapter = new LN0Adapter(null, ln0); + + TExtRef tExtRef = new TExtRef(); + tExtRef.setSrcCBName("smv1"); + tExtRef.setServiceType(TServiceType.SMV); + + //When Then + assertThatThrownBy(() -> ln0Adapter.getFCDAs(tExtRef)) + .isInstanceOf(ScdException.class) + .hasMessage("Control Block smv1 not found in /LN0"); + } + + @ParameterizedTest(name = "{0}") + @CsvSource({ + "Case without InRef,LD_WITHOUT_InRef,InRef1", + "Case with no InRef finishing with _1,LD_WITH_1_Bad_InRef,InRef4", + "Case with several ExtRef desc finishing with _1,LD_WITH_2_InRef_same_SUFFIX,InRef5" + }) + void updateDoInRef_should_return_error_message(String testName, String ldInst, String doiName) { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + LN0Adapter sourceLn0 = findLn0(new SclRootAdapter(scd), "IED_NAME1", ldInst); + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + assertThat(sclReportItems).hasSize(1) + .extracting(SclReportItem::getMessage) + .containsExactly("The DOI /SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"" + ldInst + "\"]/LN0/DOI[@name=\"" + doiName + "\"] can't be bound with an ExtRef"); + } + + @Test + void updateDoInRef_should_return_error_message_when_no_Val_in_DAI_purpose() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + LN0Adapter sourceLn0 = findLn0(new SclRootAdapter(scd), "IED_NAME1", "LD_Without_Val_in_DAI_purpose"); + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + assertThat(sclReportItems).hasSize(1) + .extracting(SclReportItem::getMessage) + .containsExactly("The DOI /SCL/IED[@name=\"IED_NAME1\"]/AccessPoint/Server/LDevice[@inst=\"" + "LD_Without_Val_in_DAI_purpose" + "\"]/LN0 can't be bound with an ExtRef"); + } + + @Test + void updateDoInRef_should_not_treat_LN0_when_DAI_name_purpose_not_compliant() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + LN0Adapter sourceLn0 = findLn0(new SclRootAdapter(scd), "IED_NAME1", "LD_Without_purpose"); + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + DOIAdapter.DAIAdapter finalSetSrcRef = sourceLn0.getDOIAdapterByName("InRef6").getDataAdapterByName(DOIAdapter.DA_NAME_SET_SRC_REF); + assertThat(finalSetSrcRef.getCurrentElem().isSetVal()).isFalse(); + assertThat(sclReportItems).isEmpty(); + } + + @Test + void updateDoInRef_should_update_setSrcRef_and_not_setSrcCB_when_one_ExtRef_desc_matches() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + LN0Adapter sourceLn0 = findLn0(sclRootAdapter, "IED_NAME1", "LD_WITH_1_InRef_without_cbName"); + String doiNameInRef = "InRef7"; + List daiValList = sourceLn0.getDOIAdapterByName(doiNameInRef).getDataAdapterByName(DOIAdapter.DA_NAME_SET_SRC_REF).getCurrentElem().getVal(); + String originalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + String expectedSrcRef = "IED_NAME1LD_WITH_1_InRef/PRANCR1.Do11.sdo11"; + + assertThat(daiValList).isEmpty(); + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + String finalSetSrcRef = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_REF); + String finalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + assertThat(finalSetSrcRef) + .isNotBlank() + .isEqualTo(expectedSrcRef); + assertThat(finalSetSrcCB).isEqualTo(originalSetSrcCB); + assertThat(sclReportItems).isEmpty(); + } + + @Test + void updateDoInRef_should_update_setSrcRef_and_setSrcCB_when_one_ExtRef_desc_matches() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + LN0Adapter sourceLn0 = findLn0(sclRootAdapter, "IED_NAME1", "LD_WITH_1_InRef"); + String doiNameInRef = "InRef2"; + String originalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + String expectedSrcRef = "IED_NAME1LD_WITH_1_InRef/PRANCR1.Do11.sdo11"; + String expectedSrcCb = "IED_NAME1LD_WITH_1_InRef/prefixANCR1.GSE1"; + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + + // Then + String finalSetSrcRef = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_REF); + String finalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + assertThat(finalSetSrcRef) + .isNotBlank() + .isEqualTo(expectedSrcRef); + assertThat(finalSetSrcCB) + .isNotEqualTo(originalSetSrcCB) + .isEqualTo(expectedSrcCb); + assertThat(sclReportItems).isEmpty(); + } + + @Test + void updateDoInRef_should_update_setSrcRef_and_setSrcCB_and_setTstRef_and_setTstCB_when_ExtRef_desc_matches() { + // Given + SCL scd = SclTestMarshaller.getSCLFromFile("/scd-test-update-inref/scd_update_inref_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + LN0Adapter sourceLn0 = findLn0(sclRootAdapter, "IED_NAME1", "LD_WITH_3_InRef"); + String doiNameInRef = "InRef3"; + String originalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + String expectedSrcRef = "IED_NAME1LD_WITH_3_InRef/PRANCR1.Do11.sdo11"; + String expectedSrcCb = "IED_NAME1LD_WITH_3_InRef/prefixANCR1.GSE1"; + String originalSetTstCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_TST_CB); + String expectedTstRef = "IED_NAME1LD_WITH_3_InRef/PRANCR1.Do11.sdo11"; + String expectedTstCB = "IED_NAME1LD_WITH_3_InRef/prefixANCR3.GSE3"; + + // When + List sclReportItems = sourceLn0.updateDoInRef(); + // Then + + String finalSetSrcRef = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_REF); + String finalSetSrcCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_SRC_CB); + String finalSetTstRef = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_TST_REF); + String finalSetTstCB = getDaiValue(sourceLn0, doiNameInRef, DOIAdapter.DA_NAME_SET_TST_CB); + assertThat(finalSetSrcRef) + .isNotBlank() + .isEqualTo(expectedSrcRef); + assertThat(finalSetSrcCB) + .isNotEqualTo(originalSetSrcCB) + .isEqualTo(expectedSrcCb); + assertThat(finalSetTstRef) + .isNotBlank() + .isEqualTo(expectedTstRef); + assertThat(finalSetTstCB) + .isNotEqualTo(originalSetTstCB) + .isEqualTo(expectedTstCB); + assertThat(sclReportItems).isEmpty(); + } } diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapterTest.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapterTest.java index c0dc0e129..199b61e32 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapterTest.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/scl/ied/LNAdapterTest.java @@ -4,6 +4,7 @@ package org.lfenergy.compas.sct.commons.scl.ied; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -20,8 +21,7 @@ import java.util.Optional; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.assertj.core.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*; class LNAdapterTest { @@ -380,7 +380,7 @@ void testUpdateExtRefSource() { assertThat(extRef.getSrcCBName()).isEqualTo(extRefInfo.getSourceInfo().getSrcCBName()); assertThat(extRef.getSrcLDInst()).isEqualTo(extRefInfo.getSourceInfo().getSrcLDInst()); - assertThat(extRef.getLnClass().contains(extRefInfo.getSourceInfo().getSrcLNClass())).isTrue(); + assertThat(extRef.getLnClass()).contains(extRefInfo.getSourceInfo().getSrcLNClass()); } @@ -434,7 +434,8 @@ void testGetLNodeName() { } @Test - void testUpdateDAI() { + void updateDAI_should_throw_ScdException_when_ResumedDataTemplate_is_empty() { + // Given ResumedDataTemplate rDtt = new ResumedDataTemplate(); SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); @@ -445,28 +446,138 @@ void testUpdateDAI() { .withLnClass(TLLN0Enum.LLN_0.value()) .build(); - assertThrows(ScdException.class, () -> lnAdapter.updateDAI(rDtt)); + // When Then + assertThatThrownBy(() -> lnAdapter.updateDAI(rDtt)) + .isInstanceOf(ScdException.class) + .hasMessage("Cannot update undefined DAI"); + } + + @Test + void updateDAI_should_throw_ScdException_when_ResumedDataTemplate_DA_name_is_not_defined() { + // Given + ResumedDataTemplate rDtt = new ResumedDataTemplate(); + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + AbstractLNAdapter lnAdapter = AbstractLNAdapter.builder() + .withLDeviceAdapter(lDeviceAdapter) + .withLnClass(TLLN0Enum.LLN_0.value()) + .build(); DoTypeName doTypeName = new DoTypeName("Do.sdo1.d"); rDtt.setDoName(doTypeName); - assertThrows(ScdException.class, () -> lnAdapter.updateDAI(rDtt)); + + // When Then + assertThatThrownBy(() -> lnAdapter.updateDAI(rDtt)) + .isInstanceOf(ScdException.class) + .hasMessage("Cannot update undefined DAI"); + } + + @Test + @Disabled(value = "Disable while bug #241 is not fixed") + /** + * @see Issue !241 (UpdateDAI Val does not produce subelements (SDI) as expected) + */ + void updateDAI_should_not_update_DAI_Val_when_DTT_Fc_not_defined() { + // Given + ResumedDataTemplate rDtt = new ResumedDataTemplate(); + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + LN0Adapter lnAdapter = (LN0Adapter) AbstractLNAdapter.builder() + .withLDeviceAdapter(lDeviceAdapter) + .withLnClass(TLLN0Enum.LLN_0.value()) + .build(); + + rDtt.setDoName(new DoTypeName("Do.sdo1.d")); rDtt.setDaName(new DaTypeName("antRef.bda1.bda2.bda3")); TVal tVal = new TVal(); tVal.setValue("newValue"); rDtt.setDaiValues(List.of(tVal)); - assertDoesNotThrow(() -> lnAdapter.updateDAI(rDtt)); - lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS2").get()); - AbstractLNAdapter lnAdapter2 = AbstractLNAdapter.builder() + // When + lnAdapter.updateDAI(rDtt); + + // Then + SDIAdapter.DAIAdapter daiAdapter = lnAdapter + .getDOIAdapterByName("Do") + .getStructuredDataAdapterByName("sdo1") + .getStructuredDataAdapterByName("d") + .getStructuredDataAdapterByName("bda1") + .getStructuredDataAdapterByName("bda2") + .getStructuredDataAdapterByName("bda3") + .getDataAdapterByName("antRef"); + + assertThat(daiAdapter.getCurrentElem().getVal().get(0).getValue()).isEqualTo("Completed-diff"); + + System.out.println(MarshallerWrapper.marshall(scd)); + } + + @Test + @Disabled(value = "Disable while bug #241 is not fixed") + /** + * @see Issue !241 (UpdateDAI Val does not produce subelements (SDI) as expected) + */ + void updateDAI_should_update_DAI_values_when_data_updatable() { + // Given + ResumedDataTemplate rDtt = new ResumedDataTemplate(); + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS2").get()); + LN0Adapter lnAdapter = (LN0Adapter) AbstractLNAdapter.builder() .withLDeviceAdapter(lDeviceAdapter) .withLnClass(TLLN0Enum.LLN_0.value()) .build(); + + rDtt.setDoName(new DoTypeName("Do.sdo1.d")); + rDtt.setDaName(new DaTypeName("antRef.bda1.bda2.bda3")); + TVal tVal = new TVal(); + tVal.setValue("newValue"); rDtt.setValImport(true); rDtt.setFc(TFCEnum.SE); - assertTrue(rDtt.isUpdatable()); - assertDoesNotThrow(() -> lnAdapter2.updateDAI(rDtt)); + assertThat(rDtt.isUpdatable()).isTrue(); + rDtt.setDaiValues(List.of(tVal)); + + // When + lnAdapter.updateDAI(rDtt); + + // Then + SDIAdapter.DAIAdapter daiAdapter = lnAdapter + .getDOIAdapterByName("Do") + .getStructuredDataAdapterByName("sdo1") + .getStructuredDataAdapterByName("d") + .getStructuredDataAdapterByName("antRef") + .getStructuredDataAdapterByName("bda1") + .getStructuredDataAdapterByName("bda2") + .getDataAdapterByName("bda3"); + + assertThat(daiAdapter.getCurrentElem().getVal().get(0).getValue()).isEqualTo("newValue"); System.out.println(MarshallerWrapper.marshall(scd)); + } + + @Test + void updateDAI_should_not_update_DAI_values_when_not_updatable() { + // Given + ResumedDataTemplate rDtt = new ResumedDataTemplate(); + SCL scd = SclTestMarshaller.getSCLFromFile("/ied-test-schema-conf/ied_unit_test.xml"); + SclRootAdapter sclRootAdapter = new SclRootAdapter(scd); + IEDAdapter iAdapter = assertDoesNotThrow(() -> sclRootAdapter.getIEDAdapterByName("IED_NAME")); + LDeviceAdapter lDeviceAdapter = assertDoesNotThrow(() -> iAdapter.findLDeviceAdapterByLdInst("LD_INS1").get()); + LN0Adapter lnAdapter = (LN0Adapter) AbstractLNAdapter.builder() + .withLDeviceAdapter(lDeviceAdapter) + .withLnClass(TLLN0Enum.LLN_0.value()) + .build(); + + rDtt.setDoName(new DoTypeName("Do.sdo1.d")); + rDtt.setDaName(new DaTypeName("antRef.bda1.bda2.bda3")); + rDtt.setValImport(false); + // When Then + assertThat(rDtt.isUpdatable()).isFalse(); + assertThatCode(() -> lnAdapter.updateDAI(rDtt)).doesNotThrowAnyException(); } @Test diff --git a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java index 0e554cb54..71d3d110a 100644 --- a/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java +++ b/sct-commons/src/test/java/org/lfenergy/compas/sct/commons/testhelpers/SclHelper.java @@ -156,4 +156,7 @@ public static Stream streamAllExtRef(SclRootAdapter sclRootAdapter) { .flatMap(List::stream); } + public static String getDaiValue(AbstractLNAdapter ln, String doiName, String daiName) { + return ln.getDOIAdapterByName(doiName).getDataAdapterByName(daiName).getCurrentElem().getVal().get(0).getValue(); + } } diff --git a/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml new file mode 100644 index 000000000..e09b36fc2 --- /dev/null +++ b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_coherent_extRefs.xml @@ -0,0 +1,44 @@ + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml new file mode 100644 index 000000000..57382209d --- /dev/null +++ b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_binded_ied_controls_fcda.xml @@ -0,0 +1,148 @@ + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml new file mode 100644 index 000000000..6c8c046b7 --- /dev/null +++ b/sct-commons/src/test/resources/limitation_cb_dataset_fcda/scd_check_limitation_ied_controls_dataset.xml @@ -0,0 +1,89 @@ + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml new file mode 100644 index 000000000..4cd987c58 --- /dev/null +++ b/sct-commons/src/test/resources/scd-extref-create-dataset-and-controlblocks/scd_create_dataset_and_controlblocks_success_analyze.xml @@ -0,0 +1,130 @@ + + + + + + +
    + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + + + + + + + + + + + 5 + + + + + + + + + + + + + + + + + + + + on + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + on + off + test + + + diff --git a/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml new file mode 100644 index 000000000..c2d4c04d1 --- /dev/null +++ b/sct-commons/src/test/resources/scd-ied-dtt-com-import-stds/scd_with_same_compas_icd_header_in_different_functions.xml @@ -0,0 +1,33 @@ + + + + + SCD + +
    + + + 0 + + + + + + + + + + + + + + + + + + + diff --git a/sct-commons/src/test/resources/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd b/sct-commons/src/test/resources/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd index 48c81222d..68a260d66 100644 --- a/sct-commons/src/test/resources/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd +++ b/sct-commons/src/test/resources/scd-refresh-lnode/issue_165_enhance_68_Test_Dai_Updatable.scd @@ -169,6 +169,80 @@ + + SAMU + SAMU + + + + + + + + + + + + + + + + + 01.00.000 + + + 01.00.000 + + + + + on + + + + + + + + + + + SAMU + SAMU + + + + + + + + + + + + + + + + + 01.00.000 + + + 01.00.000 + + + + + on + + + + + + + + + diff --git a/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml new file mode 100644 index 000000000..7b81a7164 --- /dev/null +++ b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ko.xml @@ -0,0 +1,115 @@ + + + + + + +
    + + + + + + + + + + LD_WITHOUT_InRef_DOI_InRef1 + + + + + + + + + + + LD_WITH_1_Bad_InRef_DOI_InRef4 + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_1_Bad_InRef_DOI_InRef5 + + + + OLD_VAL + + + + + + + + + + + + + + + + + + OLD_VAL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml new file mode 100644 index 000000000..9b15fc0b5 --- /dev/null +++ b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_issue_231_test_ok.xml @@ -0,0 +1,86 @@ + + + + + + +
    + + + + + + + + + + + LD_WITH_1_InRef_DOI_InRef2 + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_3_InRef_DOI_InRef3 + + + + OLD_VAL + + + + OLD_VAL + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml new file mode 100644 index 000000000..3a26f7493 --- /dev/null +++ b/sct-commons/src/test/resources/scd-test-update-inref/scd_update_inref_test.xml @@ -0,0 +1,177 @@ + + + + + + +
    + + + + + + + + + + LD_WITHOUT_InRef_DOI_InRef1 + + + + + + + + + + + LD_WITH_1_InRef_DOI_InRef2 + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_3_InRef_DOI_InRef3 + + + + OLD_VAL + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_1_Bad_InRef_DOI_InRef4 + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_1_Bad_InRef_DOI_InRef5 + + + + OLD_VAL + + + + + + + + + + + + + + + + + + OLD_VAL + + + + + + + + + + + + + + + + LD_WITH_1_InRef_without_cbName_DOI_InRef7 + + + + OLD_VAL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file