From e3f58b1f7051e391ebd87b2930813fd9a1ea0b8f Mon Sep 17 00:00:00 2001 From: Geoffroy Jamgotchian Date: Fri, 11 Oct 2019 15:22:04 +0200 Subject: [PATCH] Initial commit Signed-off-by: Geoffroy Jamgotchian --- .gitignore | 29 + .travis.yml | 21 + LICENSE | 373 ++ appveyor.yml | 22 + base-voltage-color/pom.xml | 39 + .../powsybl/basevoltage/BaseVoltageColor.java | 100 + .../basevoltage/BaseVoltageConfig.java | 61 + .../basevoltage/BaseVoltagesConfig.java | 36 + .../basevoltage/BaseVoltageColorTest.java | 58 + .../basevoltage/BaseVoltagesConfigTest.java | 46 + .../src/test/resources/base-voltages.yml | 13 + cgmes-dl/cgmes-dl-conversion/pom.xml | 72 + .../cgmes/dl/conversion/CgmesDLExporter.java | 225 ++ .../CgmesDLImportPostProcessor.java | 53 + .../cgmes/dl/conversion/CgmesDLImporter.java | 171 + .../cgmes/dl/conversion/CgmesDLModel.java | 141 + .../cgmes/dl/conversion/CgmesDLUtils.java | 67 + .../cgmes/dl/conversion/ContextUtils.java | 48 + .../cgmes/dl/conversion/ExportContext.java | 90 + ...ractCouplingDeviceDiagramDataExporter.java | 47 + .../AbstractDiagramDataExporter.java | 111 + .../AbstractInjectionDiagramDataExporter.java | 46 + .../AbstractLineDiagramDataExporter.java | 40 + .../AbstractNodeDiagramDataExporter.java | 42 + .../exporters/BusDiagramDataExporter.java | 34 + .../exporters/BusbarDiagramDataExporter.java | 53 + .../DanglingLineDiagramDataExporter.java | 33 + .../GeneratorDiagramDataExporter.java | 34 + .../HvdcLineDiagramDataExporter.java | 33 + .../exporters/LineDiagramDataExporter.java | 33 + .../exporters/LoadDiagramDataExporter.java | 34 + .../exporters/ShuntDiagramDataExporter.java | 34 + .../exporters/SvcDiagramDataExporter.java | 34 + .../exporters/SwitchDiagramDataExporter.java | 34 + .../Transformer3WDiagramDataExporter.java | 53 + .../TransformerDiagramDataExporter.java | 34 + ...ractCouplingDeviceDiagramDataImporter.java | 48 + .../AbstractInjectionDiagramDataImporter.java | 48 + .../importers/BusDiagramDataImporter.java | 69 + .../importers/BusbarDiagramDataImporter.java | 62 + .../GeneratorDiagramDataImporter.java | 53 + .../HvdcLineDiagramDataImporter.java | 53 + .../importers/LineDiagramDataImporter.java | 66 + .../importers/LoadDiagramDataImporter.java | 52 + .../importers/ShuntDiagramDataImporter.java | 52 + .../importers/SvcDiagramDataImporter.java | 52 + .../importers/SwitchDiagramDataImporter.java | 53 + .../TransformerDiagramDataImporter.java | 78 + .../src/main/resources/CGMES-DL.sparql | 281 ++ .../AbstractCgmesDLExporterTest.java | 154 + .../dl/conversion/AbstractCgmesDLTest.java | 211 ++ .../CgmesDLImportPostProcessorTest.java | 43 + .../dl/conversion/CgmesDLImporterTest.java | 297 ++ .../cgmes/dl/conversion/CgmesDLModelTest.java | 150 + ...CouplingDeviceDiagramDataExporterTest.java | 69 + ...tractInjectionDiagramDataExporterTest.java | 59 + ...stractNodeLineDiagramDataExporterTest.java | 47 + .../exporters/BusDiagramDataExporterTest.java | 48 + .../BusbarDiagramDataExporterTest.java | 57 + .../DanglingLineDiagramDataExporterTest.java | 46 + .../GeneratorDiagramDataExporterTest.java | 47 + .../HvdcLineDiagramDataExporterTest.java | 46 + .../LineDiagramDataExporterTest.java | 46 + .../LoadDiagramDataExporterTest.java | 46 + .../ShuntDiagramDataExporterTest.java | 47 + .../exporters/SvcDiagramDataExporterTest.java | 47 + .../SwitchDiagramDataExporterTest.java | 50 + .../Transformer3WDiagramDataExporterTest.java | 106 + .../TransformerDiagramDataExporterTest.java | 49 + cgmes-dl/cgmes-iidm-extensions/pom.xml | 59 + .../dl/CouplingDeviceDiagramData.java | 105 + .../iidm/extensions/dl/DiagramPoint.java | 65 + .../iidm/extensions/dl/DiagramTerminal.java | 17 + .../extensions/dl/InjectionDiagramData.java | 92 + .../iidm/extensions/dl/LineDiagramData.java | 117 + .../extensions/dl/NetworkDiagramData.java | 70 + .../iidm/extensions/dl/NodeDiagramData.java | 95 + .../ThreeWindingsTransformerDiagramData.java | 102 + .../java/com/powsybl/cgmes/iidm/Networks.java | 463 +++ ...AbstractCouplingDeviceDiagramDataTest.java | 44 + .../dl/AbstractInjectionDiagramDataTest.java | 37 + .../dl/AbstractLineDiagramDataTest.java | 68 + .../dl/AbstractNodeDiagramDataTest.java | 31 + .../extensions/dl/BusDiagramDataTest.java | 45 + .../extensions/dl/BusbarDiagramDataTest.java | 41 + .../dl/DanglingLineDiagramDataTest.java | 63 + .../dl/GeneratorDiagramDataTest.java | 42 + .../dl/HvdcLineDiagramDataTest.java | 59 + .../extensions/dl/LineDiagramDataTest.java | 65 + .../extensions/dl/LoadDiagramDataTest.java | 39 + .../extensions/dl/NetworkDiagramDataTest.java | 46 + .../extensions/dl/ShuntDiagramDataTest.java | 39 + .../StaticVarCompensatorDiagramDataTest.java | 39 + .../extensions/dl/SwitchDiagramDataTest.java | 43 + ...reeWindingsTransformerDiagramDataTest.java | 74 + .../dl/TransformerDiagramDataTest.java | 43 + cgmes-dl/pom.xml | 79 + checkstyle.xml | 73 + pom.xml | 554 +++ single-line-diagram-cgmes/pom.xml | 68 + .../sld/cgmes/AbstractCgmesLayout.java | 344 ++ .../sld/cgmes/CgmesSubstationLayout.java | 66 + .../cgmes/CgmesSubstationLayoutFactory.java | 25 + .../sld/cgmes/CgmesVoltageLevelLayout.java | 54 + .../cgmes/CgmesVoltageLevelLayoutFactory.java | 24 + .../cgmes/LayoutToCgmesDlExporterTool.java | 141 + .../LayoutToCgmesExtensionsConverter.java | 369 ++ .../AbstractCgmesVoltageLevelLayoutTest.java | 46 + .../sld/cgmes/AbstractNodeTopologyTest.java | 261 ++ .../powsybl/sld/cgmes/BusTopologyTest.java | 319 ++ .../cgmes/LayoutToCgmesExtensionsTest.java | 192 + .../NodeTopologyHorizontalBusbarTest.java | 58 + .../cgmes/NodeTopologyVerticalBusbarTest.java | 58 + .../doc/CellBlockDecomposer.adoc | 6 + .../doc/CellDetector.adoc | 128 + single-line-diagram-core/doc/Index.adoc | 23 + .../doc/PositionFinder.adoc | 86 + single-line-diagram-core/doc/Subsections.adoc | 5 + .../doc/images/CellType_INTERN.svg | 260 ++ .../doc/images/CellTypes.svg | 590 +++ single-line-diagram-core/doc/images/Cells.svg | 365 ++ .../doc/images/PowsyblLayout_HPos.svg | 3199 +++++++++++++++++ .../doc/images/busbars.svg | 578 +++ .../doc/images/rawGraph.svg | 748 ++++ .../rawGraph.svg.2019_04_01_16_43_32.0.svg | 865 +++++ .../doc/images/rawGraphExtern.svg | 835 +++++ .../doc/images/rawGraphExternShunt.svg | 768 ++++ .../doc/images/rawGraphIntern.svg | 794 ++++ .../doc/images/rawPosition.svg | 580 +++ .../doc/images/subsections.svg | 260 ++ .../images/verticalBusConnectionPattern.svg | 605 ++++ single-line-diagram-core/pom.xml | 88 + .../com/powsybl/sld/SubstationDiagram.java | 136 + .../com/powsybl/sld/VoltageLevelDiagram.java | 142 + .../sld/layout/AbstractSubstationLayout.java | 103 + .../powsybl/sld/layout/BlockOrganizer.java | 105 + .../powsybl/sld/layout/BlockPositionner.java | 223 ++ .../sld/layout/CellBlockDecomposer.java | 254 ++ .../com/powsybl/sld/layout/CellDetector.java | 22 + .../layout/HorizontalSubstationLayout.java | 174 + .../HorizontalSubstationLayoutFactory.java | 20 + .../sld/layout/ImplicitCellDetector.java | 357 ++ .../powsybl/sld/layout/InfoCalcPoints.java | 116 + .../powsybl/sld/layout/LayoutParameters.java | 358 ++ .../sld/layout/PositionByClustering.java | 691 ++++ .../powsybl/sld/layout/PositionFinder.java | 41 + .../com/powsybl/sld/layout/PositionFree.java | 586 +++ .../sld/layout/PositionFromExtension.java | 63 + .../layout/PositionVoltageLevelLayout.java | 58 + .../PositionVoltageLevelLayoutFactory.java | 74 + .../sld/layout/RandomVoltageLevelLayout.java | 48 + .../RandomVoltageLevelLayoutFactory.java | 35 + .../com/powsybl/sld/layout/SubSections.java | 417 +++ .../powsybl/sld/layout/SubstationLayout.java | 19 + .../sld/layout/SubstationLayoutFactory.java | 17 + .../sld/layout/VerticalSubstationLayout.java | 188 + .../VerticalSubstationLayoutFactory.java | 20 + .../sld/layout/VoltageLevelLayout.java | 20 + .../sld/layout/VoltageLevelLayoutFactory.java | 19 + .../sld/library/AdaptedAnchorPoint.java | 58 + .../sld/library/AdaptedComponentMetadata.java | 66 + .../sld/library/AdaptedComponentSize.java | 42 + .../sld/library/AnchorOrientation.java | 18 + .../com/powsybl/sld/library/AnchorPoint.java | 87 + .../sld/library/AnchorPointAdapter.java | 31 + .../sld/library/AnchorPointProvider.java | 19 + .../com/powsybl/sld/library/Component.java | 35 + .../powsybl/sld/library/ComponentLibrary.java | 27 + .../sld/library/ComponentMetadata.java | 59 + .../sld/library/ComponentMetadataAdapter.java | 32 + .../powsybl/sld/library/ComponentSize.java | 44 + .../sld/library/ComponentSizeAdapter.java | 30 + .../sld/library/ComponentTypeName.java | 34 + .../com/powsybl/sld/library/Components.java | 48 + .../library/ResourcesComponentLibrary.java | 83 + .../com/powsybl/sld/model/AbstractBlock.java | 207 ++ .../powsybl/sld/model/AbstractBusCell.java | 72 + .../com/powsybl/sld/model/AbstractCell.java | 104 + .../sld/model/AbstractComposedBlock.java | 87 + .../sld/model/AbstractParallelBlock.java | 63 + .../sld/model/AbstractPrimaryBlock.java | 110 + .../java/com/powsybl/sld/model/ArrowNode.java | 20 + .../java/com/powsybl/sld/model/BaseNode.java | 28 + .../java/com/powsybl/sld/model/Block.java | 102 + .../powsybl/sld/model/BodyParallelBlock.java | 73 + .../powsybl/sld/model/BodyPrimaryBlock.java | 105 + .../java/com/powsybl/sld/model/BusCell.java | 38 + .../java/com/powsybl/sld/model/BusNode.java | 91 + .../main/java/com/powsybl/sld/model/Cell.java | 50 + .../com/powsybl/sld/model/ComposedBlock.java | 19 + .../java/com/powsybl/sld/model/Coord.java | 84 + .../main/java/com/powsybl/sld/model/Edge.java | 39 + .../com/powsybl/sld/model/ExternCell.java | 60 + .../com/powsybl/sld/model/Feeder2WTNode.java | 50 + .../com/powsybl/sld/model/Feeder3WTNode.java | 94 + .../powsybl/sld/model/FeederBranchNode.java | 26 + .../com/powsybl/sld/model/FeederLineNode.java | 35 + .../com/powsybl/sld/model/FeederNode.java | 128 + .../powsybl/sld/model/Fictitious3WTNode.java | 28 + .../com/powsybl/sld/model/FictitiousNode.java | 29 + .../java/com/powsybl/sld/model/Graph.java | 957 +++++ .../com/powsybl/sld/model/InternCell.java | 188 + .../java/com/powsybl/sld/model/LegBlock.java | 23 + .../powsybl/sld/model/LegParralelBlock.java | 76 + .../powsybl/sld/model/LegPrimaryBlock.java | 92 + .../main/java/com/powsybl/sld/model/Node.java | 277 ++ .../com/powsybl/sld/model/Orientation.java | 17 + .../com/powsybl/sld/model/ParallelBlock.java | 13 + .../java/com/powsybl/sld/model/Position.java | 116 + .../com/powsybl/sld/model/PrimaryBlock.java | 17 + .../com/powsybl/sld/model/SerialBlock.java | 220 ++ .../java/com/powsybl/sld/model/ShuntCell.java | 34 + .../main/java/com/powsybl/sld/model/Side.java | 25 + .../powsybl/sld/model/SubstationGraph.java | 175 + .../com/powsybl/sld/model/SwitchNode.java | 81 + .../java/com/powsybl/sld/model/TwtEdge.java | 33 + .../com/powsybl/sld/model/UndefinedBlock.java | 65 + .../GraphBuildPostProcessor.java | 19 + .../GraphBuildPostProcessorMock.java | 31 + .../svg/DefaultNodeLabelConfiguration.java | 63 + .../com/powsybl/sld/svg/DefaultSVGWriter.java | 1179 ++++++ ...SubstationDiagramInitialValueProvider.java | 155 + ...DefaultSubstationDiagramStyleProvider.java | 119 + .../com/powsybl/sld/svg/GraphMetadata.java | 341 ++ .../com/powsybl/sld/svg/InitialValue.java | 114 + .../com/powsybl/sld/svg/LabelPosition.java | 39 + .../sld/svg/NodeLabelConfiguration.java | 18 + .../powsybl/sld/svg/SVGLoaderToDocument.java | 50 + .../java/com/powsybl/sld/svg/SVGWriter.java | 54 + ...SubstationDiagramInitialValueProvider.java | 26 + .../svg/SubstationDiagramStyleProvider.java | 39 + .../sld/svg/SubstationDiagramStyles.java | 49 + .../com/powsybl/sld/svg/WireConnection.java | 142 + ...VoltageSubstationDiagramStyleProvider.java | 111 + .../java/com/powsybl/sld/util/RGBColor.java | 135 + .../sld/util/TopologicalStyleProvider.java | 218 ++ .../2-windings-transformer.svg | 5 + .../3-windings-transformer.svg | 6 + .../resources/ConvergenceLibrary/arrow.svg | 8 + .../resources/ConvergenceLibrary/breaker.svg | 12 + .../ConvergenceLibrary/capacitor.svg | 7 + .../ConvergenceLibrary/components.css | 110 + .../ConvergenceLibrary/components.xml | 117 + .../ConvergenceLibrary/disconnector.svg | 10 + .../ConvergenceLibrary/generator.svg | 6 + .../resources/ConvergenceLibrary/inductor.svg | 6 + .../ConvergenceLibrary/load-break-switch.svg | 14 + .../resources/ConvergenceLibrary/load.svg | 6 + .../resources/ConvergenceLibrary/node.svg | 4 + .../ConvergenceLibrary/phaseShift.svg | 7 + .../main/resources/ConvergenceLibrary/svc.svg | 10 + .../main/resources/ConvergenceLibrary/vsc.svg | 7 + .../com/powsybl/sld/AbstractTestCase.java | 155 + .../test/java/com/powsybl/sld/TestCase1.java | 230 ++ .../sld/TestCase10TestBreakerToBus.java | 63 + .../sld/TestCase11SubstationGraph.java | 476 +++ .../powsybl/sld/TestCase12GraphWith3WT.java | 465 +++ .../com/powsybl/sld/TestCase1BusBreaker.java | 87 + .../com/powsybl/sld/TestCase1inverted.java | 168 + .../com/powsybl/sld/TestCase2StackedCell.java | 207 ++ .../com/powsybl/sld/TestCase3Coupling.java | 184 + .../powsybl/sld/TestCase4NotParallelel.java | 249 ++ .../powsybl/sld/TestCase5ShuntHorizontal.java | 197 + .../powsybl/sld/TestCase5ShuntVertical.java | 210 ++ .../TestCase6CouplingNonFlatHorizontal.java | 208 ++ .../sld/TestCase7CellDetectionIssue.java | 80 + .../com/powsybl/sld/TestCase7DoubleDJ.java | 78 + .../powsybl/sld/TestCase8JumpOverStacked.java | 112 + .../sld/TestCase9singularInternCell.java | 65 + .../java/com/powsybl/sld/TestSerialBlock.java | 151 + .../powsybl/sld/TestSerialParallelBlock.java | 189 + .../powsybl/sld/TestShiftFeedersPosition.java | 199 + .../powsybl/sld/TestSubstationDiagram.java | 110 + .../TestUnicityNodeIdWithMutipleNetwork.java | 115 + .../powsybl/sld/TestVoltageLevelDiagram.java | 107 + .../sld/layout/LayoutParametersTest.java | 72 + .../powsybl/sld/library/AnchorPointTest.java | 33 + .../powsybl/sld/library/ComponentsTest.java | 59 + .../powsybl/sld/svg/GraphMetadataTest.java | 122 + .../sld/svg/InitialValueProviderTest.java | 110 + .../sld/svg/NominalVoltageStyleTest.java | 93 + .../com/powsybl/sld/svg/TestRGBColor.java | 37 + .../powsybl/sld/svg/TopologicalStyleTest.java | 98 + .../src/test/resources/TestCase1.svg | 180 + .../TestCase11SubstationGraphHorizontal.svg | 1953 ++++++++++ ...tionGraphHorizontalNominalVoltageLevel.svg | 1953 ++++++++++ .../TestCase11SubstationGraphVertical.svg | 1953 ++++++++++ .../src/test/resources/TestCase12GraphVL1.svg | 878 +++++ .../TestCase12GraphVL1_optimized.svg | 580 +++ .../src/test/resources/TestCase12GraphVL2.svg | 689 ++++ .../TestCase12GraphVL2_optimized.svg | 483 +++ .../src/test/resources/TestCase12GraphVL3.svg | 603 ++++ .../TestCase12GraphVL3_optimized.svg | 439 +++ .../test/resources/TestCase1BusBreaker.svg | 173 + .../src/test/resources/TestCase1inverted.svg | 180 + .../test/resources/TestCase2StackedCell.svg | 195 + .../src/test/resources/TestCase3Coupling.svg | 175 + .../test/resources/TestCase4NotParallelel.svg | 340 ++ .../resources/TestCase5ShuntHorizontal.svg | 251 ++ .../test/resources/TestCase5ShuntVertical.svg | 247 ++ .../TestCase6CouplingNonFlatHorizontal.svg | 238 ++ .../resources/TestDefaultFeedersPosition.svg | 521 +++ .../resources/TestDefaultFeedersPosition2.svg | 509 +++ .../resources/TestShiftFeedersPosition.svg | 521 +++ .../src/test/resources/TestSubstation.svg | 281 ++ .../resources/TestUnicityNodeIdNetWork1.svg | 170 + .../resources/TestUnicityNodeIdNetWork2.svg | 170 + .../src/test/resources/TestVL.svg | 170 + .../src/test/resources/base-voltages.yml | 18 + .../src/test/resources/nominalVoltage.svg | 281 ++ .../src/test/resources/substDiag.svg | 1953 ++++++++++ .../test/resources/substDiag_metadata.json | 2878 +++++++++++++++ .../src/test/resources/topological.svg | 281 ++ .../src/test/resources/vlDiag.svg | 878 +++++ .../src/test/resources/vlDiag_metadata.json | 1402 ++++++++ single-line-diagram-iidm-extensions/pom.xml | 77 + .../extensions/BusbarSectionPosition.java | 54 + .../BusbarSectionPositionXmlSerializer.java | 72 + .../iidm/extensions/ConnectablePosition.java | 134 + .../ConnectablePositionXmlSerializer.java | 121 + .../resources/xsd/busbarSectionPosition.xsd | 20 + .../resources/xsd/connectablePosition.xsd | 37 + .../BusbarSectionPositionXmlTest.java | 65 + .../ConnectablePositionXmlTest.java | 136 + .../resources/busbarSectionPositionRef.xml | 13 + .../test/resources/connectablePositionRef.xml | 31 + single-line-diagram-util/pom.xml | 51 + .../util/SmartVoltageLevelLayoutFactory.java | 60 + .../sld/util/SubstationDiagramTool.java | 187 + single-line-diagram-view-app/pom.xml | 64 + .../app/AbstractSubstationDiagramViewer.java | 989 +++++ .../sld/view/app/SubstationDiagramViewer.java | 104 + single-line-diagram-view/pom.xml | 49 + .../view/AbstractContainerDiagramView.java | 216 ++ .../powsybl/sld/view/DisplayVoltageLevel.java | 5 + .../com/powsybl/sld/view/NodeHandler.java | 169 + .../sld/view/SubstationDiagramView.java | 37 + .../sld/view/VoltageLevelDiagramView.java | 34 + .../powsybl/sld/view/VoltageLevelHandler.java | 198 + .../com/powsybl/sld/view/WireHandler.java | 207 ++ .../src/main/resources/log4j2.xml | 13 + .../src/main/resources/logback.xml | 11 + .../com/powsybl/sld/view/WireHandlerTest.java | 67 + 343 files changed, 62443 insertions(+) create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 appveyor.yml create mode 100644 base-voltage-color/pom.xml create mode 100644 base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltageColor.java create mode 100644 base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltageConfig.java create mode 100644 base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltagesConfig.java create mode 100644 base-voltage-color/src/test/java/com/powsybl/basevoltage/BaseVoltageColorTest.java create mode 100644 base-voltage-color/src/test/java/com/powsybl/basevoltage/BaseVoltagesConfigTest.java create mode 100644 base-voltage-color/src/test/resources/base-voltages.yml create mode 100644 cgmes-dl/cgmes-dl-conversion/pom.xml create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLImportPostProcessor.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLModel.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLUtils.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/ContextUtils.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/ExportContext.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractCouplingDeviceDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractInjectionDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractLineDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractNodeDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/BusDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/BusbarDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/DanglingLineDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/GeneratorDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/HvdcLineDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/LineDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/LoadDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/ShuntDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/SvcDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/SwitchDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/Transformer3WDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/TransformerDiagramDataExporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/AbstractCouplingDeviceDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/AbstractInjectionDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/BusDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/BusbarDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/GeneratorDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/HvdcLineDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/LineDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/LoadDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/ShuntDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/SvcDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/SwitchDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/TransformerDiagramDataImporter.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/main/resources/CGMES-DL.sparql create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/AbstractCgmesDLExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/AbstractCgmesDLTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLImportPostProcessorTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLImporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLModelTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractCouplingDeviceDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractInjectionDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractNodeLineDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/BusDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/BusbarDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/DanglingLineDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/GeneratorDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/HvdcLineDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/LineDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/LoadDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/ShuntDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/SvcDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/SwitchDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/Transformer3WDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/TransformerDiagramDataExporterTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/pom.xml create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/CouplingDeviceDiagramData.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/DiagramPoint.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/DiagramTerminal.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/InjectionDiagramData.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/LineDiagramData.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/NetworkDiagramData.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/NodeDiagramData.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/ThreeWindingsTransformerDiagramData.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/Networks.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractCouplingDeviceDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractInjectionDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractLineDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractNodeDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/BusDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/BusbarDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/DanglingLineDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/GeneratorDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/HvdcLineDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/LineDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/LoadDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/NetworkDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/ShuntDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/StaticVarCompensatorDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/SwitchDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/ThreeWindingsTransformerDiagramDataTest.java create mode 100644 cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/TransformerDiagramDataTest.java create mode 100644 cgmes-dl/pom.xml create mode 100644 checkstyle.xml create mode 100644 pom.xml create mode 100644 single-line-diagram-cgmes/pom.xml create mode 100644 single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/AbstractCgmesLayout.java create mode 100644 single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesSubstationLayout.java create mode 100644 single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesSubstationLayoutFactory.java create mode 100644 single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesVoltageLevelLayout.java create mode 100644 single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesVoltageLevelLayoutFactory.java create mode 100644 single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/LayoutToCgmesDlExporterTool.java create mode 100644 single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/LayoutToCgmesExtensionsConverter.java create mode 100644 single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/AbstractCgmesVoltageLevelLayoutTest.java create mode 100644 single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/AbstractNodeTopologyTest.java create mode 100644 single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/BusTopologyTest.java create mode 100644 single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/LayoutToCgmesExtensionsTest.java create mode 100644 single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/NodeTopologyHorizontalBusbarTest.java create mode 100644 single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/NodeTopologyVerticalBusbarTest.java create mode 100644 single-line-diagram-core/doc/CellBlockDecomposer.adoc create mode 100644 single-line-diagram-core/doc/CellDetector.adoc create mode 100644 single-line-diagram-core/doc/Index.adoc create mode 100644 single-line-diagram-core/doc/PositionFinder.adoc create mode 100644 single-line-diagram-core/doc/Subsections.adoc create mode 100644 single-line-diagram-core/doc/images/CellType_INTERN.svg create mode 100644 single-line-diagram-core/doc/images/CellTypes.svg create mode 100644 single-line-diagram-core/doc/images/Cells.svg create mode 100644 single-line-diagram-core/doc/images/PowsyblLayout_HPos.svg create mode 100644 single-line-diagram-core/doc/images/busbars.svg create mode 100644 single-line-diagram-core/doc/images/rawGraph.svg create mode 100644 single-line-diagram-core/doc/images/rawGraph.svg.2019_04_01_16_43_32.0.svg create mode 100644 single-line-diagram-core/doc/images/rawGraphExtern.svg create mode 100644 single-line-diagram-core/doc/images/rawGraphExternShunt.svg create mode 100644 single-line-diagram-core/doc/images/rawGraphIntern.svg create mode 100644 single-line-diagram-core/doc/images/rawPosition.svg create mode 100644 single-line-diagram-core/doc/images/subsections.svg create mode 100644 single-line-diagram-core/doc/images/verticalBusConnectionPattern.svg create mode 100644 single-line-diagram-core/pom.xml create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/SubstationDiagram.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/VoltageLevelDiagram.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/AbstractSubstationLayout.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockOrganizer.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockPositionner.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/CellBlockDecomposer.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/CellDetector.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalSubstationLayout.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalSubstationLayoutFactory.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/ImplicitCellDetector.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/InfoCalcPoints.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LayoutParameters.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionByClustering.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFinder.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFree.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFromExtension.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayout.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayoutFactory.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/RandomVoltageLevelLayout.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/RandomVoltageLevelLayoutFactory.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubSections.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubstationLayout.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubstationLayoutFactory.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VerticalSubstationLayout.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VerticalSubstationLayoutFactory.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayout.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayoutFactory.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedAnchorPoint.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedComponentMetadata.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedComponentSize.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorOrientation.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPoint.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPointAdapter.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPointProvider.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/Component.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentLibrary.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentMetadata.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentMetadataAdapter.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentSize.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentSizeAdapter.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentTypeName.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/Components.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/library/ResourcesComponentLibrary.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractBusCell.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractCell.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractComposedBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractParallelBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractPrimaryBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/ArrowNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/BaseNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Block.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/BodyParallelBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/BodyPrimaryBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/BusCell.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/BusNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Cell.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/ComposedBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Coord.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Edge.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/ExternCell.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Feeder2WTNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Feeder3WTNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederBranchNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederLineNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Fictitious3WTNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/FictitiousNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Graph.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/InternCell.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegParralelBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegPrimaryBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Node.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Orientation.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/ParallelBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Position.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/PrimaryBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/SerialBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/ShuntCell.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/Side.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/SubstationGraph.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/SwitchNode.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/TwtEdge.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/model/UndefinedBlock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/postprocessor/GraphBuildPostProcessor.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/postprocessor/GraphBuildPostProcessorMock.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultNodeLabelConfiguration.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSVGWriter.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSubstationDiagramInitialValueProvider.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSubstationDiagramStyleProvider.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/GraphMetadata.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/InitialValue.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/LabelPosition.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/NodeLabelConfiguration.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SVGLoaderToDocument.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SVGWriter.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramInitialValueProvider.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramStyleProvider.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramStyles.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/svg/WireConnection.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/util/NominalVoltageSubstationDiagramStyleProvider.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/util/RGBColor.java create mode 100644 single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologicalStyleProvider.java create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/2-windings-transformer.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/3-windings-transformer.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/arrow.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/breaker.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/capacitor.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/components.css create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/components.xml create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/disconnector.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/generator.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/inductor.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/load-break-switch.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/load.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/node.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/phaseShift.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/svc.svg create mode 100644 single-line-diagram-core/src/main/resources/ConvergenceLibrary/vsc.svg create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/AbstractTestCase.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase10TestBreakerToBus.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase11SubstationGraph.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase12GraphWith3WT.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1BusBreaker.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1inverted.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase2StackedCell.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase3Coupling.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase4NotParallelel.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase5ShuntHorizontal.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase5ShuntVertical.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase6CouplingNonFlatHorizontal.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase7CellDetectionIssue.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase7DoubleDJ.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase8JumpOverStacked.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase9singularInternCell.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestSerialBlock.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestSerialParallelBlock.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestShiftFeedersPosition.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestSubstationDiagram.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestUnicityNodeIdWithMutipleNetwork.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/TestVoltageLevelDiagram.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/layout/LayoutParametersTest.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/library/AnchorPointTest.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/library/ComponentsTest.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/svg/GraphMetadataTest.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/svg/InitialValueProviderTest.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/svg/NominalVoltageStyleTest.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/svg/TestRGBColor.java create mode 100644 single-line-diagram-core/src/test/java/com/powsybl/sld/svg/TopologicalStyleTest.java create mode 100644 single-line-diagram-core/src/test/resources/TestCase1.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase11SubstationGraphHorizontal.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase11SubstationGraphHorizontalNominalVoltageLevel.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase11SubstationGraphVertical.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase12GraphVL1.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase12GraphVL1_optimized.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase12GraphVL2.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase12GraphVL2_optimized.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase12GraphVL3.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase12GraphVL3_optimized.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase1BusBreaker.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase1inverted.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase2StackedCell.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase3Coupling.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase4NotParallelel.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase5ShuntHorizontal.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase5ShuntVertical.svg create mode 100644 single-line-diagram-core/src/test/resources/TestCase6CouplingNonFlatHorizontal.svg create mode 100644 single-line-diagram-core/src/test/resources/TestDefaultFeedersPosition.svg create mode 100644 single-line-diagram-core/src/test/resources/TestDefaultFeedersPosition2.svg create mode 100644 single-line-diagram-core/src/test/resources/TestShiftFeedersPosition.svg create mode 100644 single-line-diagram-core/src/test/resources/TestSubstation.svg create mode 100644 single-line-diagram-core/src/test/resources/TestUnicityNodeIdNetWork1.svg create mode 100644 single-line-diagram-core/src/test/resources/TestUnicityNodeIdNetWork2.svg create mode 100644 single-line-diagram-core/src/test/resources/TestVL.svg create mode 100644 single-line-diagram-core/src/test/resources/base-voltages.yml create mode 100644 single-line-diagram-core/src/test/resources/nominalVoltage.svg create mode 100644 single-line-diagram-core/src/test/resources/substDiag.svg create mode 100644 single-line-diagram-core/src/test/resources/substDiag_metadata.json create mode 100644 single-line-diagram-core/src/test/resources/topological.svg create mode 100644 single-line-diagram-core/src/test/resources/vlDiag.svg create mode 100644 single-line-diagram-core/src/test/resources/vlDiag_metadata.json create mode 100644 single-line-diagram-iidm-extensions/pom.xml create mode 100644 single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/BusbarSectionPosition.java create mode 100644 single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/BusbarSectionPositionXmlSerializer.java create mode 100644 single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/ConnectablePosition.java create mode 100644 single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/ConnectablePositionXmlSerializer.java create mode 100644 single-line-diagram-iidm-extensions/src/main/resources/xsd/busbarSectionPosition.xsd create mode 100644 single-line-diagram-iidm-extensions/src/main/resources/xsd/connectablePosition.xsd create mode 100644 single-line-diagram-iidm-extensions/src/test/java/com/powsybl/sld/iidm/extensions/BusbarSectionPositionXmlTest.java create mode 100644 single-line-diagram-iidm-extensions/src/test/java/com/powsybl/sld/iidm/extensions/ConnectablePositionXmlTest.java create mode 100644 single-line-diagram-iidm-extensions/src/test/resources/busbarSectionPositionRef.xml create mode 100644 single-line-diagram-iidm-extensions/src/test/resources/connectablePositionRef.xml create mode 100644 single-line-diagram-util/pom.xml create mode 100644 single-line-diagram-util/src/main/java/com/powsybl/sld/util/SmartVoltageLevelLayoutFactory.java create mode 100644 single-line-diagram-util/src/main/java/com/powsybl/sld/util/SubstationDiagramTool.java create mode 100644 single-line-diagram-view-app/pom.xml create mode 100644 single-line-diagram-view-app/src/main/java/com/powsybl/sld/view/app/AbstractSubstationDiagramViewer.java create mode 100644 single-line-diagram-view-app/src/main/java/com/powsybl/sld/view/app/SubstationDiagramViewer.java create mode 100644 single-line-diagram-view/pom.xml create mode 100644 single-line-diagram-view/src/main/java/com/powsybl/sld/view/AbstractContainerDiagramView.java create mode 100644 single-line-diagram-view/src/main/java/com/powsybl/sld/view/DisplayVoltageLevel.java create mode 100644 single-line-diagram-view/src/main/java/com/powsybl/sld/view/NodeHandler.java create mode 100644 single-line-diagram-view/src/main/java/com/powsybl/sld/view/SubstationDiagramView.java create mode 100644 single-line-diagram-view/src/main/java/com/powsybl/sld/view/VoltageLevelDiagramView.java create mode 100644 single-line-diagram-view/src/main/java/com/powsybl/sld/view/VoltageLevelHandler.java create mode 100644 single-line-diagram-view/src/main/java/com/powsybl/sld/view/WireHandler.java create mode 100644 single-line-diagram-view/src/main/resources/log4j2.xml create mode 100644 single-line-diagram-view/src/main/resources/logback.xml create mode 100644 single-line-diagram-view/src/test/java/com/powsybl/sld/view/WireHandlerTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..ea3c9b25d --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Maven projects +cgmes-dl/target +cgmes-dl/cgmes-dl-conversion/target +cgmes-dl/cgmes-iidm-extensions/target +cgmes-gl/target +cgmes-gl/cgmes-gl-conversion/target +cgmes-gl/cgmes-gl-iidm-extensions/target +substation-diagram/substation-diagram-cgmes/target +substation-diagram/substation-diagram-core/target +substation-diagram/substation-diagram-util/target +substation-diagram/substation-diagram-view/target +substation-diagram/target +substation-webviewer/target +target + +# IntelliJ +/.idea +*.iml + +# Compilation settings +install.cfg + +# Eclipse projects +.classpath +.project +org.eclipse.core.resources.prefs +org.eclipse.jdt.core.prefs +org.eclipse.m2e.core.prefs +org.eclipse.jdt.groovy.core.prefs diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..bc4893c2e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,21 @@ +language: java + +dist: trusty +sudo: required + +jdk: +- oraclejdk8 + +addons: + sonarcloud: + organization: "powsybl-ci-github" + token: + secure: ${SONAR_TOKEN} + +install: +# Build powsybl-core +- git clone https://github.com/powsybl/powsybl-core powsybl/powsybl-core +- pushd powsybl/powsybl-core && mvn --batch-mode -DskipTests install && popd + +script: +- mvn --batch-mode -Pjacoco,checks clean verify sonar:sonar diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..a612ad981 --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..e78f6a1f2 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,22 @@ +version: '{build}' +os: Windows Server 2012 +install: + - ps: | + Add-Type -AssemblyName System.IO.Compression.FileSystem + if (!(Test-Path -Path "C:\maven" )) { + (new-object System.Net.WebClient).DownloadFile( + 'http://www.us.apache.org/dist/maven/maven-3/3.2.5/binaries/apache-maven-3.2.5-bin.zip', + 'C:\maven-bin.zip' + ) + [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\maven-bin.zip", "C:\maven") + } + - cmd: SET M2_HOME=C:\maven\apache-maven-3.2.5 + - cmd: SET PATH=%M2_HOME%\bin;%JAVA_HOME%\bin;%PATH% +before_build: + - git clone https://github.com/powsybl/powsybl-core.git powsybl/powsybl-core + - ps: | + pushd powsybl/powsybl-core + mvn -DskipTests install --batch-mode + popd +build_script: + - mvn clean package --batch-mode diff --git a/base-voltage-color/pom.xml b/base-voltage-color/pom.xml new file mode 100644 index 000000000..673b7065a --- /dev/null +++ b/base-voltage-color/pom.xml @@ -0,0 +1,39 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-single-line-diagram + 1.0.0-SNAPSHOT + + + powsybl-base-voltage-color + Base Voltages color configuration + + + + com.powsybl + powsybl-commons + ${powsyblcore.version} + + + + junit + junit + ${junit.version} + test + + + + diff --git a/base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltageColor.java b/base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltageColor.java new file mode 100644 index 000000000..d9e71cef7 --- /dev/null +++ b/base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltageColor.java @@ -0,0 +1,100 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.basevoltage; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; + +import com.powsybl.commons.config.PlatformConfig; + +/** + * + * @author Massimo Ferraro + */ +public class BaseVoltageColor { + + private static final String CONFIG_FILE = "base-voltages.yml"; + + private final BaseVoltagesConfig config; + + public BaseVoltageColor(Path configFile) throws IOException { + Objects.requireNonNull(configFile); + Yaml yaml = new Yaml(new Constructor(BaseVoltagesConfig.class)); + InputStream configInputStream = Files.newInputStream(configFile); + config = yaml.load(configInputStream); + } + + public BaseVoltageColor() throws IOException { + this(PlatformConfig.defaultConfig().getConfigDir().resolve(CONFIG_FILE)); + } + + public List getProfiles() { + return config.getBaseVoltages() + .stream() + .map(BaseVoltageConfig::getProfile) + .distinct() + .collect(Collectors.toList()); + } + + public String getDefaultProfile() { + return config.getDefaultProfile(); + } + + public List getBaseVoltageNames(String profile) { + Objects.requireNonNull(profile); + return config.getBaseVoltages() + .stream() + .filter(baseVoltage -> baseVoltage.getProfile().equals(profile)) + .map(BaseVoltageConfig::getName) + .collect(Collectors.toList()); + } + + public String getBaseVoltageName(double voltage, String profile) { + Objects.requireNonNull(profile); + return config.getBaseVoltages() + .stream() + .filter(baseVoltage -> baseVoltage.getProfile().equals(profile) + && baseVoltage.getMinValue() <= voltage + && baseVoltage.getMaxValue() > voltage) + .map(BaseVoltageConfig::getName) + .findFirst() + .orElse(null); + } + + public String getColor(String baseVoltageName, String profile) { + Objects.requireNonNull(baseVoltageName); + Objects.requireNonNull(profile); + return config.getBaseVoltages() + .stream() + .filter(baseVoltage -> baseVoltage.getProfile().equals(profile) + && baseVoltage.getName().equals(baseVoltageName)) + .map(BaseVoltageConfig::getColor) + .findFirst() + .orElse(null); + } + + public String getColor(double voltage, String profile) { + Objects.requireNonNull(profile); + return config.getBaseVoltages() + .stream() + .filter(baseVoltage -> baseVoltage.getProfile().equals(profile) + && baseVoltage.getMinValue() <= voltage + && baseVoltage.getMaxValue() > voltage) + .map(BaseVoltageConfig::getColor) + .findFirst() + .orElse(null); + } + +} diff --git a/base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltageConfig.java b/base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltageConfig.java new file mode 100644 index 000000000..4440b2743 --- /dev/null +++ b/base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltageConfig.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.basevoltage; + +/** +* +* @author Massimo Ferraro +*/ +public class BaseVoltageConfig { + + private String name; + private double minValue; + private double maxValue; + private String color; + private String profile; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public double getMinValue() { + return minValue; + } + + public void setMinValue(double minValue) { + this.minValue = minValue; + } + + public double getMaxValue() { + return maxValue; + } + + public void setMaxValue(double maxValue) { + this.maxValue = maxValue; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public String getProfile() { + return profile; + } + + public void setProfile(String profile) { + this.profile = profile; + } + +} diff --git a/base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltagesConfig.java b/base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltagesConfig.java new file mode 100644 index 000000000..876c2c738 --- /dev/null +++ b/base-voltage-color/src/main/java/com/powsybl/basevoltage/BaseVoltagesConfig.java @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.basevoltage; + +import java.util.List; + +/** + * + * @author Massimo Ferraro + */ +public class BaseVoltagesConfig { + + private List baseVoltages; + private String defaultProfile; + + public List getBaseVoltages() { + return baseVoltages; + } + + public void setBaseVoltages(List baseVoltages) { + this.baseVoltages = baseVoltages; + } + + public String getDefaultProfile() { + return defaultProfile; + } + + public void setDefaultProfile(String defaultProfile) { + this.defaultProfile = defaultProfile; + } + +} diff --git a/base-voltage-color/src/test/java/com/powsybl/basevoltage/BaseVoltageColorTest.java b/base-voltage-color/src/test/java/com/powsybl/basevoltage/BaseVoltageColorTest.java new file mode 100644 index 000000000..275393842 --- /dev/null +++ b/base-voltage-color/src/test/java/com/powsybl/basevoltage/BaseVoltageColorTest.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.basevoltage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; + +import org.junit.Test; + +/** + * + * @author Massimo Ferraro + */ +public class BaseVoltageColorTest { + + @Test + public void test() throws IOException, URISyntaxException { + Path configFile = Paths.get(getClass().getResource("/base-voltages.yml").toURI()); + BaseVoltageColor baseVoltageColor = new BaseVoltageColor(configFile); + // getProfiles + assertEquals(Arrays.asList("RTE"), baseVoltageColor.getProfiles()); + // getDefaultProfile + assertEquals("RTE", baseVoltageColor.getDefaultProfile()); + // getBaseVoltageNames + assertEquals(Arrays.asList("400", "225"), baseVoltageColor.getBaseVoltageNames("RTE")); + // getBaseVoltageName + assertNull(baseVoltageColor.getBaseVoltageName(500, "RTE")); + assertEquals("400", baseVoltageColor.getBaseVoltageName(450, "RTE")); + assertEquals("400", baseVoltageColor.getBaseVoltageName(400, "RTE")); + assertEquals("400", baseVoltageColor.getBaseVoltageName(300, "RTE")); + assertEquals("225", baseVoltageColor.getBaseVoltageName(250, "RTE")); + assertEquals("225", baseVoltageColor.getBaseVoltageName(180, "RTE")); + assertNull(baseVoltageColor.getBaseVoltageName(150, "RTE")); + // getColor by name + assertEquals("#BBBBBBB", baseVoltageColor.getColor("400", "RTE")); + assertEquals("#AAAAAAA", baseVoltageColor.getColor("225", "RTE")); + assertNull(baseVoltageColor.getColor("150", "RTE")); + // getColor by voltage + assertNull(baseVoltageColor.getColor(500, "RTE")); + assertEquals("#BBBBBBB", baseVoltageColor.getColor(450, "RTE")); + assertEquals("#BBBBBBB", baseVoltageColor.getColor(400, "RTE")); + assertEquals("#BBBBBBB", baseVoltageColor.getColor(300, "RTE")); + assertEquals("#AAAAAAA", baseVoltageColor.getColor(250, "RTE")); + assertEquals("#AAAAAAA", baseVoltageColor.getColor(180, "RTE")); + assertNull(baseVoltageColor.getColor(150, "RTE")); + } + +} diff --git a/base-voltage-color/src/test/java/com/powsybl/basevoltage/BaseVoltagesConfigTest.java b/base-voltage-color/src/test/java/com/powsybl/basevoltage/BaseVoltagesConfigTest.java new file mode 100644 index 000000000..7de6d68fa --- /dev/null +++ b/base-voltage-color/src/test/java/com/powsybl/basevoltage/BaseVoltagesConfigTest.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.basevoltage; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.IOException; +import java.io.InputStream; + +import org.junit.Test; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; + +/** + * + * @author Massimo Ferraro + */ +public class BaseVoltagesConfigTest { + + @Test + public void test() throws IOException { + Yaml yaml = new Yaml(new Constructor(BaseVoltagesConfig.class)); + InputStream configInputStream = getClass().getResourceAsStream("/base-voltages.yml"); + BaseVoltagesConfig config = yaml.load(configInputStream); + assertNotNull(config); + assertNotNull(config.getBaseVoltages()); + assertEquals(2, config.getBaseVoltages().size()); + assertEquals("400", config.getBaseVoltages().get(0).getName()); + assertEquals(300, config.getBaseVoltages().get(0).getMinValue(), 0); + assertEquals(500, config.getBaseVoltages().get(0).getMaxValue(), 0); + assertEquals("#BBBBBBB", config.getBaseVoltages().get(0).getColor()); + assertEquals("RTE", config.getBaseVoltages().get(0).getProfile()); + assertEquals("225", config.getBaseVoltages().get(1).getName()); + assertEquals(180, config.getBaseVoltages().get(1).getMinValue(), 0); + assertEquals(300, config.getBaseVoltages().get(1).getMaxValue(), 0); + assertEquals("#AAAAAAA", config.getBaseVoltages().get(1).getColor()); + assertEquals("RTE", config.getBaseVoltages().get(1).getProfile()); + assertEquals("RTE", config.getDefaultProfile()); + } + +} diff --git a/base-voltage-color/src/test/resources/base-voltages.yml b/base-voltage-color/src/test/resources/base-voltages.yml new file mode 100644 index 000000000..e11af6d37 --- /dev/null +++ b/base-voltage-color/src/test/resources/base-voltages.yml @@ -0,0 +1,13 @@ +baseVoltages: + - name: "400" + minValue: 300 + maxValue: 500 + color: "#BBBBBBB" + profile: "RTE" + - name: "225" + minValue: 180 + maxValue: 300 + color: "#AAAAAAA" + profile: "RTE" + +defaultProfile: "RTE" \ No newline at end of file diff --git a/cgmes-dl/cgmes-dl-conversion/pom.xml b/cgmes-dl/cgmes-dl-conversion/pom.xml new file mode 100644 index 000000000..f77211df4 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/pom.xml @@ -0,0 +1,72 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-cgmes-dl + 1.0.0-SNAPSHOT + + + powsybl-cgmes-dl-conversion + CGMES DL conversion + + + + com.powsybl + powsybl-cgmes-conversion + + + com.powsybl + powsybl-cgmes-iidm-extensions + ${project.version} + + + com.powsybl + powsybl-cgmes-model + + + com.powsybl + powsybl-iidm-api + + + com.powsybl + powsybl-triple-store-api + + + + com.powsybl + powsybl-cgmes-iidm-extensions + ${project.version} + test-jar + test + + + com.powsybl + powsybl-iidm-impl + test + + + junit + junit + test + + + org.mockito + mockito-all + test + + + org.slf4j + slf4j-simple + test + + + \ No newline at end of file diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLExporter.java new file mode 100644 index 000000000..038781f4c --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLExporter.java @@ -0,0 +1,225 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import com.powsybl.triplestore.api.PrefixNamespace; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.dl.conversion.exporters.BusDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.BusbarDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.DanglingLineDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.GeneratorDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.HvdcLineDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.LineDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.LoadDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.ShuntDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.SvcDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.SwitchDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.Transformer3WDiagramDataExporter; +import com.powsybl.cgmes.dl.conversion.exporters.TransformerDiagramDataExporter; +import com.powsybl.cgmes.model.CgmesNamespace; +import com.powsybl.commons.datasource.DataSource; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.TripleStore; +import com.powsybl.triplestore.api.TripleStoreFactory; + +/** +* +* @author Massimo Ferraro +*/ +public class CgmesDLExporter { + + private static final Logger LOG = LoggerFactory.getLogger(CgmesDLExporter.class); + public static final String MD_NAMESPACE = "http://iec.ch/TC57/61970-552/ModelDescription/1#"; + + private Network network; + private TripleStore tripleStore; + private CgmesDLModel cgmesDLModel; + + public CgmesDLExporter(Network network, TripleStore tripleStore, CgmesDLModel cgmesDLModel) { + this.network = Objects.requireNonNull(network); + this.tripleStore = Objects.requireNonNull(tripleStore); + this.cgmesDLModel = Objects.requireNonNull(cgmesDLModel); + } + + public CgmesDLExporter(Network network, TripleStore tripleStore) { + this(network, tripleStore, new CgmesDLModel(tripleStore)); + } + + public CgmesDLExporter(Network network) { + this(network, TripleStoreFactory.create()); + } + + public void exportDLData(DataSource dataSource) { + Objects.requireNonNull(dataSource); + ExportContext context = new ExportContext(dataSource, tripleStore); + Map terminals = getTerminals(); + Map busbarNodes = getBusbarNodes(); + addNamespaces(context); + addModel(context); + addDiagrams(context); + exportNodesDLData(context, busbarNodes); + exportLinesDLData(context); + exportDanglingLinesDLData(context); + exportGeneratorsDLData(context, terminals); + exportLoadsDLData(context, terminals); + exportShuntsDLData(context, terminals); + exportSvcsDLData(context, terminals); + exportTransformersDLData(context, terminals); + exportSwitchesDLData(context, terminals); + exportTransformers3WDLData(context, terminals); + exportHvdcLinesDLData(context); + tripleStore.write(dataSource); + } + + private Map getTerminals() { + Map terminals = new HashMap<>(); + cgmesDLModel.getTerminals().forEach(terminal -> + terminals.put(terminal.getId("equipment") + "_" + terminal.get("terminalSide"), terminal.getId("terminal")) + ); + return terminals; + } + + private Map getBusbarNodes() { + Map busbarNodes = new HashMap<>(); + cgmesDLModel.getBusbarNodes().forEach(busbarNode -> + busbarNodes.put(busbarNode.getId("busbarSection"), busbarNode.getId("busbarNode")) + ); + return busbarNodes; + } + + private void addNamespaces(ExportContext context) { + if (!namespaceAlreadyExist("data")) { + tripleStore.addNamespace("data", context.getBaseNamespace()); + } + if (!namespaceAlreadyExist("cim")) { + tripleStore.addNamespace("cim", CgmesNamespace.CIM_16_NAMESPACE); + } + if (!namespaceAlreadyExist("md")) { + tripleStore.addNamespace("md", MD_NAMESPACE); + } + } + + private boolean namespaceAlreadyExist(String prefix) { + return tripleStore.getNamespaces().stream().map(PrefixNamespace::getPrefix).anyMatch(prefix::equals); + } + + private void addModel(ExportContext context) { + PropertyBag modelProperties = new PropertyBag(Arrays.asList(CgmesDLModel.MODEL_SCENARIO_TIME, CgmesDLModel.MODEL_CREATED, CgmesDLModel.MODEL_DESCRIPTION, "" + + CgmesDLModel.MODEL_VERSION, CgmesDLModel.MODEL_PROFILE, CgmesDLModel.MODEL_DEPENDENT_ON)); + modelProperties.setResourceNames(Arrays.asList(CgmesDLModel.MODEL_DEPENDENT_ON)); + modelProperties.setClassPropertyNames(Arrays.asList(CgmesDLModel.MODEL_SCENARIO_TIME, CgmesDLModel.MODEL_CREATED, CgmesDLModel.MODEL_DESCRIPTION, CgmesDLModel.MODEL_VERSION, CgmesDLModel.MODEL_PROFILE, CgmesDLModel.MODEL_DEPENDENT_ON)); + modelProperties.put(CgmesDLModel.MODEL_SCENARIO_TIME, network.getCaseDate().toString()); + modelProperties.put(CgmesDLModel.MODEL_CREATED, new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").format(new Date())); + modelProperties.put(CgmesDLModel.MODEL_DESCRIPTION, network.getName()); + modelProperties.put(CgmesDLModel.MODEL_VERSION, "1"); + modelProperties.put(CgmesDLModel.MODEL_PROFILE, "http://entsoe.eu/CIM/DiagramLayout/3/1"); + modelProperties.put(CgmesDLModel.MODEL_DEPENDENT_ON, network.getId()); + tripleStore.add(context.getDlContext(), MD_NAMESPACE, "FullModel", modelProperties); + } + + private void addDiagrams(ExportContext context) { + NetworkDiagramData.getDiagramsNames(network).forEach(diagramName -> { + PropertyBag diagramObjectProperties = new PropertyBag(Arrays.asList(CgmesDLModel.IDENTIFIED_OBJECT_NAME, CgmesDLModel.ORIENTATION)); + diagramObjectProperties.setResourceNames(Arrays.asList(CgmesDLModel.ORIENTATION)); + diagramObjectProperties.setClassPropertyNames(Arrays.asList(CgmesDLModel.IDENTIFIED_OBJECT_NAME)); + diagramObjectProperties.put(CgmesDLModel.IDENTIFIED_OBJECT_NAME, diagramName); + diagramObjectProperties.put(CgmesDLModel.ORIENTATION, CgmesNamespace.CIM_16_NAMESPACE + "OrientationKind.negative"); + String diagramId = tripleStore.add(context.getDlContext(), CgmesNamespace.CIM_16_NAMESPACE, "Diagram", diagramObjectProperties); + context.setDiagramId(diagramId, diagramName); + }); + } + + private void exportNodesDLData(ExportContext context, Map busbarNodes) { + LOG.info("Exporting Nodes DL Data"); + network.getVoltageLevelStream().forEach(voltageLavel -> { + switch (voltageLavel.getTopologyKind()) { + case NODE_BREAKER: + BusbarDiagramDataExporter busbarDiagramDataExporter = new BusbarDiagramDataExporter(tripleStore, context, busbarNodes); + voltageLavel.getNodeBreakerView().getBusbarSectionStream().forEach(busbarDiagramDataExporter::exportDiagramData); + break; + case BUS_BREAKER: + BusDiagramDataExporter busDiagramDataExporter = new BusDiagramDataExporter(tripleStore, context); + voltageLavel.getBusBreakerView().getBusStream().forEach(busDiagramDataExporter::exportDiagramData); + break; + default: + throw new AssertionError("Unexpected topology kind: " + voltageLavel.getTopologyKind()); + } + }); + } + + private void exportLinesDLData(ExportContext context) { + LOG.info("Exporting Lines DL Data"); + LineDiagramDataExporter diagramDataExporter = new LineDiagramDataExporter(tripleStore, context); + network.getLineStream().forEach(diagramDataExporter::exportDiagramData); + } + + private void exportDanglingLinesDLData(ExportContext context) { + LOG.info("Exporting Dangling Lines DL Data"); + DanglingLineDiagramDataExporter diagramDataExporter = new DanglingLineDiagramDataExporter(tripleStore, context); + network.getDanglingLineStream().forEach(diagramDataExporter::exportDiagramData); + } + + private void exportGeneratorsDLData(ExportContext context, Map terminals) { + LOG.info("Exporting Generators DL Data"); + GeneratorDiagramDataExporter diagramDataExporter = new GeneratorDiagramDataExporter(tripleStore, context, terminals); + network.getGeneratorStream().forEach(diagramDataExporter::exportDiagramData); + } + + private void exportLoadsDLData(ExportContext context, Map terminals) { + LOG.info("Exporting Loads DL Data"); + LoadDiagramDataExporter diagramDataExporter = new LoadDiagramDataExporter(tripleStore, context, terminals); + network.getLoadStream().forEach(diagramDataExporter::exportDiagramData); + } + + private void exportShuntsDLData(ExportContext context, Map terminals) { + LOG.info("Exporting Shunts DL Data"); + ShuntDiagramDataExporter diagramDataExporter = new ShuntDiagramDataExporter(tripleStore, context, terminals); + network.getShuntCompensatorStream().forEach(diagramDataExporter::exportDiagramData); + } + + private void exportSvcsDLData(ExportContext context, Map terminals) { + LOG.info("Exporting SVCs DL Data"); + SvcDiagramDataExporter diagramDataExporter = new SvcDiagramDataExporter(tripleStore, context, terminals); + network.getStaticVarCompensatorStream().forEach(diagramDataExporter::exportDiagramData); + } + + private void exportTransformersDLData(ExportContext context, Map terminals) { + LOG.info("Exporting Transformers DL Data"); + TransformerDiagramDataExporter diagramDataExporter = new TransformerDiagramDataExporter(tripleStore, context, terminals); + network.getTwoWindingsTransformerStream().forEach(diagramDataExporter::exportDiagramData); + } + + private void exportSwitchesDLData(ExportContext context, Map terminals) { + LOG.info("Exporting Switches DL Data"); + SwitchDiagramDataExporter diagramDataExporter = new SwitchDiagramDataExporter(tripleStore, context, terminals); + network.getSwitchStream().forEach(diagramDataExporter::exportDiagramData); + } + + private void exportTransformers3WDLData(ExportContext context, Map terminals) { + LOG.info("Exporting Transformers 3W DL Data"); + Transformer3WDiagramDataExporter diagramDataExporter = new Transformer3WDiagramDataExporter(tripleStore, context, terminals); + network.getThreeWindingsTransformerStream().forEach(diagramDataExporter::exportDiagramData); + } + + private void exportHvdcLinesDLData(ExportContext context) { + LOG.info("Exporting HVDC Lines DL Data"); + HvdcLineDiagramDataExporter diagramDataExporter = new HvdcLineDiagramDataExporter(tripleStore, context); + network.getHvdcLineStream().forEach(diagramDataExporter::exportDiagramData); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLImportPostProcessor.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLImportPostProcessor.java new file mode 100644 index 000000000..d19a127d5 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLImportPostProcessor.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.auto.service.AutoService; +import com.powsybl.cgmes.conversion.CgmesImportPostProcessor; +import com.powsybl.cgmes.conversion.Profiling; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.QueryCatalog; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +@AutoService(CgmesImportPostProcessor.class) +public class CgmesDLImportPostProcessor implements CgmesImportPostProcessor { + + private static final String NAME = "cgmesDLImport"; + private static final Logger LOG = LoggerFactory.getLogger(CgmesDLImportPostProcessor.class); + + private final QueryCatalog queryCatalog; + + CgmesDLImportPostProcessor(QueryCatalog queryCatalog) { + this.queryCatalog = Objects.requireNonNull(queryCatalog); + } + + public CgmesDLImportPostProcessor() { + this(new QueryCatalog("CGMES-DL.sparql")); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public void process(Network network, TripleStore tripleStore, Profiling profiling) { + LOG.info("Execute {} CGMES import post processor on network {}", getName(), network.getId()); + CgmesDLModel cgmesDLModel = new CgmesDLModel(tripleStore, queryCatalog); + new CgmesDLImporter(network, cgmesDLModel, profiling).importDLData(); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLImporter.java new file mode 100644 index 000000000..33a94f45c --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLImporter.java @@ -0,0 +1,171 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.conversion.Profiling; +import com.powsybl.cgmes.dl.conversion.importers.BusDiagramDataImporter; +import com.powsybl.cgmes.dl.conversion.importers.BusbarDiagramDataImporter; +import com.powsybl.cgmes.dl.conversion.importers.GeneratorDiagramDataImporter; +import com.powsybl.cgmes.dl.conversion.importers.HvdcLineDiagramDataImporter; +import com.powsybl.cgmes.dl.conversion.importers.LineDiagramDataImporter; +import com.powsybl.cgmes.dl.conversion.importers.LoadDiagramDataImporter; +import com.powsybl.cgmes.dl.conversion.importers.ShuntDiagramDataImporter; +import com.powsybl.cgmes.dl.conversion.importers.SvcDiagramDataImporter; +import com.powsybl.cgmes.dl.conversion.importers.SwitchDiagramDataImporter; +import com.powsybl.cgmes.dl.conversion.importers.TransformerDiagramDataImporter; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class CgmesDLImporter { + + private static final Logger LOG = LoggerFactory.getLogger(CgmesDLImporter.class); + + private Network network; + private CgmesDLModel cgmesDLModel; + private Map terminalsDiagramData = new HashMap<>(); + private Profiling profiling; + private boolean logProfile = false; + + public CgmesDLImporter(Network network, CgmesDLModel cgmesDLModel) { + this(network, cgmesDLModel, new Profiling()); + logProfile = true; + } + + public CgmesDLImporter(Network network, CgmesDLModel cgmesDLModel, Profiling profiling) { + this.network = Objects.requireNonNull(network); + this.cgmesDLModel = Objects.requireNonNull(cgmesDLModel); + this.profiling = Objects.requireNonNull(profiling); + } + + public void importDLData() { + importTerminalsDLData(); + importBusesDLData(); + importBusbarsDLData(); + importLinesDLData(); + importGeneratorsDLData(); + importLoadsDLData(); + importShuntsDLData(); + importSwitchesDLData(); + importTransformersDLData(); + importHvdcLinesDLData(); + importSvcsDLData(); + if (logProfile) { + profiling.report(); + } + } + + private void importTerminalsDLData() { + LOG.info("Importing Terminals DL Data"); + profiling.start(); + cgmesDLModel.getTerminalsDiagramData().forEach(terminalDiagramData -> { + String terminalKey = terminalDiagramData.getId("terminalEquipment") + "_" + terminalDiagramData.get("terminalSide"); + PropertyBags equipmentTerminalsDiagramData = new PropertyBags(); + if (terminalsDiagramData.containsKey(terminalKey)) { + equipmentTerminalsDiagramData = terminalsDiagramData.get(terminalKey); + } + equipmentTerminalsDiagramData.add(terminalDiagramData); + terminalsDiagramData.put(terminalKey, equipmentTerminalsDiagramData); + }); + profiling.end("DLTerminals"); + } + + private void importBusesDLData() { + LOG.info("Importing Buses DL Data"); + profiling.start(); + BusDiagramDataImporter diagramDataImporter = new BusDiagramDataImporter(network); + cgmesDLModel.getBusesDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLBuses"); + } + + private void importBusbarsDLData() { + LOG.info("Importing Busbars DL Data"); + profiling.start(); + BusbarDiagramDataImporter diagramDataImporter = new BusbarDiagramDataImporter(network); + cgmesDLModel.getBusbarsDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLBusbars"); + } + + private void importLinesDLData() { + LOG.info("Importing Lines DL Data"); + profiling.start(); + LineDiagramDataImporter diagramDataImporter = new LineDiagramDataImporter(network); + cgmesDLModel.getLinesDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLLines"); + } + + private void importGeneratorsDLData() { + LOG.info("Importing Generators DL Data"); + profiling.start(); + GeneratorDiagramDataImporter diagramDataImporter = new GeneratorDiagramDataImporter(network, terminalsDiagramData); + cgmesDLModel.getGeneratorsDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLGenerators"); + } + + private void importLoadsDLData() { + LOG.info("Importing Loads DL Data"); + profiling.start(); + LoadDiagramDataImporter diagramDataImporter = new LoadDiagramDataImporter(network, terminalsDiagramData); + cgmesDLModel.getLoadsDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLLoads"); + } + + private void importShuntsDLData() { + LOG.info("Importing Shunts DL Data"); + profiling.start(); + ShuntDiagramDataImporter diagramDataImporter = new ShuntDiagramDataImporter(network, terminalsDiagramData); + cgmesDLModel.getShuntsDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLShunts"); + } + + private void importSwitchesDLData() { + LOG.info("Importing Switches DL Data"); + profiling.start(); + SwitchDiagramDataImporter diagramDataImporter = new SwitchDiagramDataImporter(network, terminalsDiagramData); + cgmesDLModel.getSwitchesDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLSwitches"); + } + + private void importTransformersDLData() { + LOG.info("Importing Transformers DL Data"); + profiling.start(); + TransformerDiagramDataImporter diagramDataImporter = new TransformerDiagramDataImporter(network, terminalsDiagramData); + cgmesDLModel.getTransformersDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLTransformers"); + } + + private void importHvdcLinesDLData() { + LOG.info("Importing HVDC Lines DL Data"); + profiling.start(); + HvdcLineDiagramDataImporter diagramDataImporter = new HvdcLineDiagramDataImporter(network); + cgmesDLModel.getHvdcLinesDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLHVDCLines"); + } + + private void importSvcsDLData() { + LOG.info("Importing Svcs DL Data"); + profiling.start(); + SvcDiagramDataImporter diagramDataImporter = new SvcDiagramDataImporter(network, terminalsDiagramData); + cgmesDLModel.getSvcsDiagramData().forEach(diagramDataImporter::importDiagramData); + profiling.end("DLSVCs"); + } + + public Network getNetworkWithDLData() { + return network; + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLModel.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLModel.java new file mode 100644 index 000000000..2dc9a133e --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLModel.java @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.model.CgmesNamespace; +import com.powsybl.triplestore.api.PropertyBags; +import com.powsybl.triplestore.api.QueryCatalog; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class CgmesDLModel { + + public static final String TERMINAL_DIAGRAM_DATA_QUERY_KEY = "terminalDiagramData"; + public static final String BUS_DIAGRAM_DATA_QUERY_KEY = "busDiagramData"; + public static final String BUSBAR_DIAGRAM_DATA_QUERY_KEY = "busbarDiagramData"; + public static final String LINE_DIAGRAM_DATA_QUERY_KEY = "lineDiagramData"; + public static final String GENERATOR_DIAGRAM_DATA_QUERY_KEY = "generatorDiagramData"; + public static final String LOAD_DIAGRAM_DATA_QUERY_KEY = "loadDiagramData"; + public static final String SHUNT_DIAGRAM_DATA_QUERY_KEY = "shuntDiagramData"; + public static final String SWITCH_DIAGRAM_DATA_QUERY_KEY = "switchDiagramData"; + public static final String TRANSFORMER_DIAGRAM_DATA_QUERY_KEY = "transformerDiagramData"; + public static final String HVDC_LINE_DIAGRAM_DATA_QUERY_KEY = "hvdcLineDiagramData"; + public static final String SVC_DIAGRAM_DATA_QUERY_KEY = "svcDiagramData"; + public static final String TERMINALS_QUERY_KEY = "terminals"; + public static final String BUSBAR_NODES_QUERY_KEY = "busbarNodes"; + public static final String MODEL_DESCRIPTION = "Model.description"; + public static final String MODEL_DEPENDENT_ON = "Model.DependentOn"; + public static final String MODEL_VERSION = "Model.version"; + public static final String MODEL_PROFILE = "Model.profile"; + public static final String MODEL_CREATED = "Model.created"; + public static final String MODEL_SCENARIO_TIME = "Model.scenarioTime"; + public static final String IDENTIFIED_OBJECT_NAME = "IdentifiedObject.name"; + public static final String ORIENTATION = "orientation"; + public static final String IDENTIFIED_OBJECT = "IdentifiedObject"; + public static final String DIAGRAM = "Diagram"; + public static final String DIAGRAM_OBJECT_STYLE = "DiagramObjectStyle"; + public static final String DIAGRAM_OBJECT = "DiagramObject"; + public static final String DIAGRAM_NAME = "diagramName"; + + private static final Logger LOG = LoggerFactory.getLogger(CgmesDLModel.class); + + private final TripleStore tripleStore; + private final QueryCatalog queryCatalog; + + public CgmesDLModel(TripleStore tripleStore) { + this(tripleStore, new QueryCatalog("CGMES-DL.sparql")); + } + + public CgmesDLModel(TripleStore tripleStore, QueryCatalog queryCatalog) { + this.tripleStore = Objects.requireNonNull(tripleStore); + tripleStore.defineQueryPrefix("cim", CgmesNamespace.CIM_16_NAMESPACE); + this.queryCatalog = Objects.requireNonNull(queryCatalog); + } + + private PropertyBags queryTripleStore(String queryKey) { + String query = queryCatalog.get(queryKey); + if (query == null) { + LOG.warn("Query [{}] not found in catalog", queryKey); + return new PropertyBags(); + } + return tripleStore.query(query); + } + + public PropertyBags getTerminalsDiagramData() { + LOG.info("Querying triple store for terminals diagram data"); + return queryTripleStore(TERMINAL_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getBusesDiagramData() { + LOG.info("Querying triple store for buses diagram data"); + return queryTripleStore(BUS_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getBusbarsDiagramData() { + LOG.info("Querying triple store for busbars diagram data"); + return queryTripleStore(BUSBAR_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getLinesDiagramData() { + LOG.info("Querying triple store for lines diagram data"); + return queryTripleStore(LINE_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getGeneratorsDiagramData() { + LOG.info("Querying triple store for generators diagram data"); + return queryTripleStore(GENERATOR_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getLoadsDiagramData() { + LOG.info("Querying triple store for loads diagram data"); + return queryTripleStore(LOAD_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getShuntsDiagramData() { + LOG.info("Querying triple store for shunts diagram data"); + return queryTripleStore(SHUNT_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getSwitchesDiagramData() { + LOG.info("Querying triple store for switches diagram data"); + return queryTripleStore(SWITCH_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getTransformersDiagramData() { + LOG.info("Querying triple store for transformers diagram data"); + return queryTripleStore(TRANSFORMER_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getHvdcLinesDiagramData() { + LOG.info("Querying triple store for HVDC lines diagram data"); + return queryTripleStore(HVDC_LINE_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getSvcsDiagramData() { + LOG.info("Querying triple store for SVCs diagram data"); + return queryTripleStore(SVC_DIAGRAM_DATA_QUERY_KEY); + } + + public PropertyBags getTerminals() { + LOG.info("Querying triple store for terminals"); + return queryTripleStore(TERMINALS_QUERY_KEY); + } + + public PropertyBags getBusbarNodes() { + LOG.info("Querying triple store for busbar nodes"); + return queryTripleStore(BUSBAR_NODES_QUERY_KEY); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLUtils.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLUtils.java new file mode 100644 index 000000000..b13180f57 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/CgmesDLUtils.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import com.powsybl.cgmes.conversion.CgmesModelExtension; +import com.powsybl.cgmes.iidm.extensions.dl.*; +import com.powsybl.cgmes.model.CgmesSubset; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.TripleStore; + +import java.util.Objects; + +/** + * @author Christian Biasuzzi + */ +public final class CgmesDLUtils { + private CgmesDLUtils() { + } + + //remove all the iidm cgmes extensions + public static void removeIidmCgmesExtensions(Network network) { + Objects.requireNonNull(network); + network.getHvdcLineStream().forEach(l -> l.removeExtension(LineDiagramData.class)); + network.getThreeWindingsTransformerStream().forEach(t -> t.removeExtension(ThreeWindingsTransformerDiagramData.class)); + network.getTwoWindingsTransformerStream().forEach(t -> t.removeExtension(CouplingDeviceDiagramData.class)); + network.getStaticVarCompensatorStream().forEach(t -> t.removeExtension(InjectionDiagramData.class)); + network.getShuntCompensatorStream().forEach(t -> t.removeExtension(InjectionDiagramData.class)); + network.getLoadStream().forEach(t -> t.removeExtension(InjectionDiagramData.class)); + network.getGeneratorStream().forEach(t -> t.removeExtension(InjectionDiagramData.class)); + network.getDanglingLineStream().forEach(t -> t.removeExtension(LineDiagramData.class)); + network.getLineStream().forEach(t -> t.removeExtension(LineDiagramData.class)); + network.getVoltageLevelStream().forEach(voltageLevel -> { + switch (voltageLevel.getTopologyKind()) { + case NODE_BREAKER: + voltageLevel.getNodeBreakerView().getBusbarSectionStream().forEach(busBarSection -> busBarSection.removeExtension(NodeDiagramData.class)); + break; + case BUS_BREAKER: + voltageLevel.getBusBreakerView().getBusStream().forEach(bus -> bus.removeExtension(NodeDiagramData.class)); + break; + default: + throw new AssertionError("Unexpected topology kind: " + voltageLevel.getTopologyKind()); + } + }); + network.getSwitchStream().forEach(sw -> sw.removeExtension(CouplingDeviceDiagramData.class)); + network.removeExtension(NetworkDiagramData.class); + } + + //retrieve, if exists, the tripleStore currently available from the CGMES model in the IIDM CGMES extensions + public static TripleStore getTripleStore(Network network) { + Objects.requireNonNull(network); + CgmesModelExtension ext = network.getExtension(CgmesModelExtension.class); + return (ext != null) ? ext.getCgmesModel().tripleStore() : null; + } + + //remove, if exists, the CGMES DL profile data from the network's CGMES tiplestore + public static void clearCgmesDl(Network network) { + Objects.requireNonNull(network); + TripleStore tStore = getTripleStore(network); + if (tStore != null) { + tStore.contextNames().stream().filter(CgmesSubset.DIAGRAM_LAYOUT::isValidName).findFirst().ifPresent(tStore::clear); + } + } +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/ContextUtils.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/ContextUtils.java new file mode 100644 index 000000000..b3b6db100 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/ContextUtils.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import com.powsybl.cgmes.model.CgmesSubset; +import com.powsybl.triplestore.api.TripleStore; + +import java.util.Objects; + +/** + * + * @author Massimo Ferraro + */ +public final class ContextUtils { + + private ContextUtils() { + } + + public static String contextNameFor(CgmesSubset subset, TripleStore tripleStore, String modelId) { + Objects.requireNonNull(subset); + Objects.requireNonNull(tripleStore); + Objects.requireNonNull(modelId); + String contextNameEQ = contextNameForEquipmentSubset(tripleStore); + return contextNameEQ != null + ? buildContextNameForSubsetFrom(contextNameEQ, subset) + : modelId + "_" + subset.getIdentifier() + ".xml"; + } + + private static String contextNameForEquipmentSubset(TripleStore tripleStore) { + String eq = CgmesSubset.EQUIPMENT.getIdentifier(); + String eqBD = CgmesSubset.EQUIPMENT_BOUNDARY.getIdentifier(); + for (String contextName : tripleStore.contextNames()) { + if (contextName.contains(eq) && !contextName.contains(eqBD)) { + return contextName; + } + } + return null; + } + + private static String buildContextNameForSubsetFrom(String contextNameEQ, CgmesSubset subset) { + String eq = CgmesSubset.EQUIPMENT.getIdentifier(); + return contextNameEQ.replace(eq, subset.getIdentifier()); + } +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/ExportContext.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/ExportContext.java new file mode 100644 index 000000000..4f8e4b2eb --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/ExportContext.java @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import java.util.*; + +import com.powsybl.cgmes.model.CgmesSubset; +import com.powsybl.commons.datasource.DataSource; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class ExportContext { + + private final String basename; + private final String baseNamespace; + private final String dlContext; + private String busBranchDiagramObjectStyleId; + private String nodeBreakerDiagramObjectStyleId; + Map diagrams = new HashMap<>(); + + public ExportContext(DataSource dataSource, TripleStore tripleStore) { + Objects.requireNonNull(dataSource); + Objects.requireNonNull(tripleStore); + this.basename = dataSource.getBaseName(); + this.baseNamespace = "http://" + dataSource.getBaseName().toLowerCase() + "/#"; + this.dlContext = ContextUtils.contextNameFor(CgmesSubset.DIAGRAM_LAYOUT, tripleStore, basename); + } + + public String getBasename() { + return basename; + } + + public String getBaseNamespace() { + return baseNamespace; + } + + public String getDlContext() { + return dlContext; + } + + public void setDiagramId(String diagramId, String diagramName) { + Objects.requireNonNull(diagramId); + Objects.requireNonNull(diagramName); + this.diagrams.put(diagramName, diagramId); + } + + public String getDiagramId(String diagramName) { + return diagrams.get(diagramName); + } + + public List getDiagramsIds() { + return new ArrayList<>(diagrams.keySet()); + } + + public List getDiagramsNames() { + return new ArrayList<>(diagrams.values()); + } + + public void setBusBranchDiagramObjectStyleId(String busBranchDiagramObjectStyleId) { + this.busBranchDiagramObjectStyleId = Objects.requireNonNull(busBranchDiagramObjectStyleId); + } + + public String getBusBranchDiagramObjectStyleId() { + return busBranchDiagramObjectStyleId; + } + + public boolean hasBusBranchDiagramObjectStyleId() { + return busBranchDiagramObjectStyleId != null && !busBranchDiagramObjectStyleId.trim().isEmpty(); + } + + public void setNodeBreakerDiagramObjectStyleId(String nodeBreakerDiagramObjectStyleId) { + this.nodeBreakerDiagramObjectStyleId = Objects.requireNonNull(nodeBreakerDiagramObjectStyleId); + } + + public String getNodeBreakerDiagramObjectStyleId() { + return nodeBreakerDiagramObjectStyleId; + } + + public boolean hasNodeBreakerDiagramObjectStyleId() { + return nodeBreakerDiagramObjectStyleId != null && !nodeBreakerDiagramObjectStyleId.trim().isEmpty(); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractCouplingDeviceDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractCouplingDeviceDiagramDataExporter.java new file mode 100644 index 000000000..003d4e30a --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractCouplingDeviceDiagramDataExporter.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.CouplingDeviceDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramTerminal; +import com.powsybl.triplestore.api.TripleStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.Objects; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractCouplingDeviceDiagramDataExporter extends AbstractDiagramDataExporter { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractCouplingDeviceDiagramDataExporter.class); + + public AbstractCouplingDeviceDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map terminals) { + super(tripleStore, context); + super.terminals = Objects.requireNonNull(terminals); + } + + protected void addDiagramData(String id, String name, CouplingDeviceDiagramData diagramData, String diagramObjectStyleId) { + if (diagramData != null) { + diagramData.getDiagramsNames().forEach(diagramName -> { + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails details = diagramData.getData(diagramName); + String diagramId = context.getDiagramId(diagramName); + String diagramObjectId = addDiagramObject(id, name, details.getRotation(), diagramObjectStyleId, diagramId); + addDiagramObjectPoint(diagramObjectId, details.getPoint()); + addTerminalData(id, name, 1, details.getTerminalPoints(DiagramTerminal.TERMINAL1), diagramObjectStyleId, diagramId); + addTerminalData(id, name, 2, details.getTerminalPoints(DiagramTerminal.TERMINAL2), diagramObjectStyleId, diagramId); + }); + } else { + LOG.warn("Coupling device {}, name {} has no diagram data, skipping export", id, name); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractDiagramDataExporter.java new file mode 100644 index 000000000..5f13e399a --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractDiagramDataExporter.java @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +import com.powsybl.cgmes.dl.conversion.CgmesDLModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.model.CgmesNamespace; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractDiagramDataExporter { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractDiagramDataExporter.class); + + protected TripleStore tripleStore; + protected ExportContext context; + protected Map terminals; + + public AbstractDiagramDataExporter(TripleStore tripleStore, ExportContext context) { + this.tripleStore = Objects.requireNonNull(tripleStore); + this.context = Objects.requireNonNull(context); + } + + protected String addDiagramObject(String id, String name, double rotation, String diagramObjectStyleId, String diagramId) { + PropertyBag diagramObjectProperties = new PropertyBag(Arrays.asList(CgmesDLModel.IDENTIFIED_OBJECT_NAME, CgmesDLModel.IDENTIFIED_OBJECT, "rotation", CgmesDLModel.DIAGRAM, CgmesDLModel.DIAGRAM_OBJECT_STYLE)); + diagramObjectProperties.setResourceNames(Arrays.asList(CgmesDLModel.IDENTIFIED_OBJECT, CgmesDLModel.DIAGRAM, CgmesDLModel.DIAGRAM_OBJECT_STYLE)); + diagramObjectProperties.setClassPropertyNames(Arrays.asList(CgmesDLModel.IDENTIFIED_OBJECT_NAME)); + diagramObjectProperties.put(CgmesDLModel.IDENTIFIED_OBJECT_NAME, name); + diagramObjectProperties.put(CgmesDLModel.IDENTIFIED_OBJECT, id); + diagramObjectProperties.put("rotation", Double.toString(rotation)); + diagramObjectProperties.put(CgmesDLModel.DIAGRAM, diagramId); + diagramObjectProperties.put(CgmesDLModel.DIAGRAM_OBJECT_STYLE, diagramObjectStyleId); + return tripleStore.add(context.getDlContext(), CgmesNamespace.CIM_16_NAMESPACE, CgmesDLModel.DIAGRAM_OBJECT, diagramObjectProperties); + } + + protected void addDiagramObjectPoint(String diagramObjectId, DiagramPoint point) { + PropertyBag diagramObjectPointProperties = new PropertyBag(Arrays.asList(CgmesDLModel.DIAGRAM_OBJECT, "sequenceNumber", "xPosition", "yPosition")); + diagramObjectPointProperties.setResourceNames(Arrays.asList(CgmesDLModel.DIAGRAM_OBJECT)); + diagramObjectPointProperties.put(CgmesDLModel.DIAGRAM_OBJECT, diagramObjectId); + diagramObjectPointProperties.put("sequenceNumber", Integer.toString(point.getSeq())); + diagramObjectPointProperties.put("xPosition", Double.toString(point.getX())); + diagramObjectPointProperties.put("yPosition", Double.toString(point.getY())); + tripleStore.add(context.getDlContext(), CgmesNamespace.CIM_16_NAMESPACE, "DiagramObjectPoint", diagramObjectPointProperties); + } + + protected String addDiagramObjectStyle(String name) { + PropertyBag diagramObjectStyleProperties = new PropertyBag(Arrays.asList(CgmesDLModel.IDENTIFIED_OBJECT_NAME)); + diagramObjectStyleProperties.setClassPropertyNames(Arrays.asList(CgmesDLModel.IDENTIFIED_OBJECT_NAME)); + diagramObjectStyleProperties.put(CgmesDLModel.IDENTIFIED_OBJECT_NAME, name); + return tripleStore.add(context.getDlContext(), CgmesNamespace.CIM_16_NAMESPACE, CgmesDLModel.DIAGRAM_OBJECT_STYLE, diagramObjectStyleProperties); + } + + protected String addDiagramObjectStyle(TopologyKind topologyKind) { + switch (topologyKind) { + case NODE_BREAKER: + if (context.hasNodeBreakerDiagramObjectStyleId()) { + return context.getNodeBreakerDiagramObjectStyleId(); + } + String nodeBreakerDiagramObjectStyleId = addDiagramObjectStyle("node-breaker"); + context.setNodeBreakerDiagramObjectStyleId(nodeBreakerDiagramObjectStyleId); + return nodeBreakerDiagramObjectStyleId; + case BUS_BREAKER: + if (context.hasBusBranchDiagramObjectStyleId()) { + return context.getBusBranchDiagramObjectStyleId(); + } + String busBranchdiagramObjectStyleId = addDiagramObjectStyle("bus-branch"); + context.setBusBranchDiagramObjectStyleId(busBranchdiagramObjectStyleId); + return busBranchdiagramObjectStyleId; + default: + throw new AssertionError("Unexpected topology kind: " + topologyKind); + } + } + + protected void addTerminalData(String id, String name, int side, List terminalPoints, String diagramObjectStyleId, String diagramId) { + String diagramObjectId = addDiagramObject(getTerminalId(id, side), getTerminalName(name, side), 0, diagramObjectStyleId, diagramId); + terminalPoints.forEach(point -> addDiagramObjectPoint(diagramObjectId, point)); + } + + protected String getTerminalId(String equipmentId, int terminalSide) { + String terminalKey = equipmentId + "_" + terminalSide; + if (terminals.containsKey(terminalKey)) { + return terminals.get(terminalKey); + } + LOG.warn("Cannot find terminal id of equipment {} side {} in triple store: creating new id", equipmentId, terminalSide); + return "_" + UUID.randomUUID().toString(); + } + + protected String getTerminalName(String equipmentName, int terminalSide) { + return equipmentName + "_" + (terminalSide - 1); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractInjectionDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractInjectionDiagramDataExporter.java new file mode 100644 index 000000000..14ca1f224 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractInjectionDiagramDataExporter.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Map; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractInjectionDiagramDataExporter extends AbstractDiagramDataExporter { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractInjectionDiagramDataExporter.class); + + public AbstractInjectionDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map terminals) { + super(tripleStore, context); + super.terminals = Objects.requireNonNull(terminals); + } + + protected void addDiagramData(String id, String name, InjectionDiagramData diagramData, String diagramObjectStyleId) { + if (diagramData != null) { + diagramData.getDiagramsNames().forEach(diagramName -> { + InjectionDiagramData.InjectionDiagramDetails details = diagramData.getData(diagramName); + String diagramId = context.getDiagramId(diagramName); + String diagramObjectId = addDiagramObject(id, name, details.getRotation(), diagramObjectStyleId, diagramId); + addDiagramObjectPoint(diagramObjectId, details.getPoint()); + addTerminalData(id, name, 1, details.getTerminalPoints(), diagramObjectStyleId, diagramId); + }); + } else { + LOG.warn("Injection {}, name {} has no diagram data, skipping export", id, name); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractLineDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractLineDiagramDataExporter.java new file mode 100644 index 000000000..cddd9f251 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractLineDiagramDataExporter.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractLineDiagramDataExporter extends AbstractDiagramDataExporter { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractLineDiagramDataExporter.class); + + public AbstractLineDiagramDataExporter(TripleStore tripleStore, ExportContext context) { + super(tripleStore, context); + } + + protected void addDiagramData(String id, String name, LineDiagramData diagramData, String diagramObjectStyleId) { + if (diagramData != null) { + diagramData.getDiagramsNames().forEach(diagramName -> { + String diagramId = context.getDiagramId(diagramName); + String diagramObjectId = addDiagramObject(id, name, 0, diagramObjectStyleId, diagramId); + diagramData.getPoints(diagramName).forEach(point -> addDiagramObjectPoint(diagramObjectId, point)); + }); + } else { + LOG.warn("Line {}, name {} has no diagram data, skipping export", id, name); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractNodeDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractNodeDiagramDataExporter.java new file mode 100644 index 000000000..7ff6a318b --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractNodeDiagramDataExporter.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractNodeDiagramDataExporter extends AbstractDiagramDataExporter { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractNodeDiagramDataExporter.class); + + public AbstractNodeDiagramDataExporter(TripleStore tripleStore, ExportContext context) { + super(tripleStore, context); + } + + protected void addDiagramData(String id, String name, NodeDiagramData diagramData, String diagramObjectStyleId) { + if (diagramData != null) { + diagramData.getDiagramsNames().forEach(diagramName -> { + NodeDiagramData.NodeDiagramDataDetails details = diagramData.getData(diagramName); + String diagramId = context.getDiagramId(diagramName); + String diagramObjectId = addDiagramObject(id, name, 0, diagramObjectStyleId, diagramId); + addDiagramObjectPoint(diagramObjectId, details.getPoint1()); + addDiagramObjectPoint(diagramObjectId, details.getPoint2()); + }); + } else { + LOG.warn("Node {}, name {} has no diagram data, skipping export", id, name); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/BusDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/BusDiagramDataExporter.java new file mode 100644 index 000000000..9cfabc07d --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/BusDiagramDataExporter.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class BusDiagramDataExporter extends AbstractNodeDiagramDataExporter { + + public BusDiagramDataExporter(TripleStore tripleStore, ExportContext context) { + super(tripleStore, context); + } + + public void exportDiagramData(Bus bus) { + Objects.requireNonNull(bus); + NodeDiagramData busDiagramData = bus.getExtension(NodeDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(TopologyKind.BUS_BREAKER); + addDiagramData(bus.getId(), bus.getName(), busDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/BusbarDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/BusbarDiagramDataExporter.java new file mode 100644 index 000000000..dedd064c3 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/BusbarDiagramDataExporter.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class BusbarDiagramDataExporter extends AbstractNodeDiagramDataExporter { + + private static final Logger LOG = LoggerFactory.getLogger(BusbarDiagramDataExporter.class); + + private Map busbarNodes; + + public BusbarDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map busbarNodes) { + super(tripleStore, context); + this.busbarNodes = Objects.requireNonNull(busbarNodes); + } + + public void exportDiagramData(BusbarSection busbar) { + Objects.requireNonNull(busbar); + NodeDiagramData busbarDiagramData = busbar.getExtension(NodeDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(TopologyKind.NODE_BREAKER); + String busbarNodeId = getBusbarNodeId(busbar.getId()); + addDiagramData(busbarNodeId, busbar.getName(), busbarDiagramData, diagramObjectStyleId); + } + + protected String getBusbarNodeId(String busbarId) { + if (busbarNodes.containsKey(busbarId)) { + return busbarNodes.get(busbarId); + } + LOG.warn("Cannot find node id of busbar {} in triple store: creating new id", busbarId); + return "_" + UUID.randomUUID().toString(); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/DanglingLineDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/DanglingLineDiagramDataExporter.java new file mode 100644 index 000000000..3c8111bea --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/DanglingLineDiagramDataExporter.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class DanglingLineDiagramDataExporter extends AbstractLineDiagramDataExporter { + + public DanglingLineDiagramDataExporter(TripleStore tripleStore, ExportContext context) { + super(tripleStore, context); + } + + public void exportDiagramData(DanglingLine danglingLine) { + Objects.requireNonNull(danglingLine); + LineDiagramData danglingLineDiagramData = danglingLine.getExtension(LineDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(danglingLine.getTerminal().getVoltageLevel().getTopologyKind()); + addDiagramData(danglingLine.getId(), danglingLine.getName(), danglingLineDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/GeneratorDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/GeneratorDiagramDataExporter.java new file mode 100644 index 000000000..0d77df7a2 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/GeneratorDiagramDataExporter.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.Generator; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class GeneratorDiagramDataExporter extends AbstractInjectionDiagramDataExporter { + + public GeneratorDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map terminals) { + super(tripleStore, context, terminals); + } + + public void exportDiagramData(Generator generator) { + Objects.requireNonNull(generator); + InjectionDiagramData generatorDiagramData = generator.getExtension(InjectionDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(generator.getTerminal().getVoltageLevel().getTopologyKind()); + addDiagramData(generator.getId(), generator.getName(), generatorDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/HvdcLineDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/HvdcLineDiagramDataExporter.java new file mode 100644 index 000000000..893506785 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/HvdcLineDiagramDataExporter.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class HvdcLineDiagramDataExporter extends AbstractLineDiagramDataExporter { + + public HvdcLineDiagramDataExporter(TripleStore tripleStore, ExportContext context) { + super(tripleStore, context); + } + + public void exportDiagramData(HvdcLine hvdcLine) { + Objects.requireNonNull(hvdcLine); + LineDiagramData hvdcLineDiagramData = hvdcLine.getExtension(LineDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(hvdcLine.getConverterStation1().getTerminal().getVoltageLevel().getTopologyKind()); + addDiagramData(hvdcLine.getId(), hvdcLine.getName(), hvdcLineDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/LineDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/LineDiagramDataExporter.java new file mode 100644 index 000000000..760cd99b1 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/LineDiagramDataExporter.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.iidm.network.Line; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class LineDiagramDataExporter extends AbstractLineDiagramDataExporter { + + public LineDiagramDataExporter(TripleStore tripleStore, ExportContext context) { + super(tripleStore, context); + } + + public void exportDiagramData(Line line) { + Objects.requireNonNull(line); + LineDiagramData lineDiagramData = line.getExtension(LineDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(line.getTerminal1().getVoltageLevel().getTopologyKind()); + addDiagramData(line.getId(), line.getName(), lineDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/LoadDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/LoadDiagramDataExporter.java new file mode 100644 index 000000000..94b4a510b --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/LoadDiagramDataExporter.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.Load; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class LoadDiagramDataExporter extends AbstractInjectionDiagramDataExporter { + + public LoadDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map terminals) { + super(tripleStore, context, terminals); + } + + public void exportDiagramData(Load load) { + Objects.requireNonNull(load); + InjectionDiagramData loadDiagramData = load.getExtension(InjectionDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(load.getTerminal().getVoltageLevel().getTopologyKind()); + addDiagramData(load.getId(), load.getName(), loadDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/ShuntDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/ShuntDiagramDataExporter.java new file mode 100644 index 000000000..d95fd9fd7 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/ShuntDiagramDataExporter.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class ShuntDiagramDataExporter extends AbstractInjectionDiagramDataExporter { + + public ShuntDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map terminals) { + super(tripleStore, context, terminals); + } + + public void exportDiagramData(ShuntCompensator shunt) { + Objects.requireNonNull(shunt); + InjectionDiagramData generatorDiagramData = shunt.getExtension(InjectionDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(shunt.getTerminal().getVoltageLevel().getTopologyKind()); + addDiagramData(shunt.getId(), shunt.getName(), generatorDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/SvcDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/SvcDiagramDataExporter.java new file mode 100644 index 000000000..6c319ae6d --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/SvcDiagramDataExporter.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.StaticVarCompensator; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class SvcDiagramDataExporter extends AbstractInjectionDiagramDataExporter { + + public SvcDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map terminals) { + super(tripleStore, context, terminals); + } + + public void exportDiagramData(StaticVarCompensator svc) { + Objects.requireNonNull(svc); + InjectionDiagramData generatorDiagramData = svc.getExtension(InjectionDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(svc.getTerminal().getVoltageLevel().getTopologyKind()); + addDiagramData(svc.getId(), svc.getName(), generatorDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/SwitchDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/SwitchDiagramDataExporter.java new file mode 100644 index 000000000..9f16ee90d --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/SwitchDiagramDataExporter.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.CouplingDeviceDiagramData; +import com.powsybl.iidm.network.Switch; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class SwitchDiagramDataExporter extends AbstractCouplingDeviceDiagramDataExporter { + + public SwitchDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map terminals) { + super(tripleStore, context, terminals); + } + + public void exportDiagramData(Switch sw) { + Objects.requireNonNull(sw); + CouplingDeviceDiagramData switchDiagramData = sw.getExtension(CouplingDeviceDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(sw.getVoltageLevel().getTopologyKind()); + addDiagramData(sw.getId(), sw.getName(), switchDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/Transformer3WDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/Transformer3WDiagramDataExporter.java new file mode 100644 index 000000000..30b2aacaf --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/Transformer3WDiagramDataExporter.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Map; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramTerminal; +import com.powsybl.cgmes.iidm.extensions.dl.ThreeWindingsTransformerDiagramData; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class Transformer3WDiagramDataExporter extends AbstractDiagramDataExporter { + + private static final Logger LOG = LoggerFactory.getLogger(Transformer3WDiagramDataExporter.class); + + public Transformer3WDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map terminals) { + super(tripleStore, context); + super.terminals = Objects.requireNonNull(terminals); + } + + public void exportDiagramData(ThreeWindingsTransformer transformer) { + Objects.requireNonNull(transformer); + ThreeWindingsTransformerDiagramData transformerDiagramData = transformer.getExtension(ThreeWindingsTransformerDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(transformer.getLeg1().getTerminal().getVoltageLevel().getTopologyKind()); + if (transformerDiagramData != null) { + transformerDiagramData.getDiagramsNames().forEach(diagramName -> { + ThreeWindingsTransformerDiagramData.ThreeWindingsTransformerDiagramDataDetails details = transformerDiagramData.getData(diagramName); + String diagramId = context.getDiagramId(diagramName); + String diagramObjectId = addDiagramObject(transformer.getId(), transformer.getName(), details.getRotation(), diagramObjectStyleId, diagramId); + addDiagramObjectPoint(diagramObjectId, details.getPoint()); + addTerminalData(transformer.getId(), transformer.getName(), 1, details.getTerminalPoints(DiagramTerminal.TERMINAL1), diagramObjectStyleId, diagramId); + addTerminalData(transformer.getId(), transformer.getName(), 2, details.getTerminalPoints(DiagramTerminal.TERMINAL2), diagramObjectStyleId, diagramId); + addTerminalData(transformer.getId(), transformer.getName(), 3, details.getTerminalPoints(DiagramTerminal.TERMINAL3), diagramObjectStyleId, diagramId); + }); + } else { + LOG.warn("Transformer {}, name {} has no diagram data, skipping export", transformer.getId(), transformer.getName()); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/TransformerDiagramDataExporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/TransformerDiagramDataExporter.java new file mode 100644 index 000000000..32e3cd9d8 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/exporters/TransformerDiagramDataExporter.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.ExportContext; +import com.powsybl.cgmes.iidm.extensions.dl.CouplingDeviceDiagramData; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class TransformerDiagramDataExporter extends AbstractCouplingDeviceDiagramDataExporter { + + public TransformerDiagramDataExporter(TripleStore tripleStore, ExportContext context, Map terminals) { + super(tripleStore, context, terminals); + } + + public void exportDiagramData(TwoWindingsTransformer transformer) { + Objects.requireNonNull(transformer); + CouplingDeviceDiagramData transformerDiagramData = transformer.getExtension(CouplingDeviceDiagramData.class); + String diagramObjectStyleId = addDiagramObjectStyle(transformer.getTerminal1().getVoltageLevel().getTopologyKind()); + addDiagramData(transformer.getId(), transformer.getName(), transformerDiagramData, diagramObjectStyleId); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/AbstractCouplingDeviceDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/AbstractCouplingDeviceDiagramDataImporter.java new file mode 100644 index 000000000..d4aba146e --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/AbstractCouplingDeviceDiagramDataImporter.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Map; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.CouplingDeviceDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramTerminal; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractCouplingDeviceDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractCouplingDeviceDiagramDataImporter.class); + + protected Network network; + protected Map terminalsDiagramData; + + public AbstractCouplingDeviceDiagramDataImporter(Network network, Map terminalsDiagramData) { + this.network = Objects.requireNonNull(network); + this.terminalsDiagramData = Objects.requireNonNull(terminalsDiagramData); + } + + protected void addTerminalPoints(String equipmentId, String equipmentName, DiagramTerminal terminal, String terminalSide, CouplingDeviceDiagramData.CouplingDeviceDiagramDetails diagramDetails) { + String terminalKey = equipmentId + "_" + terminalSide; + if (terminalsDiagramData.containsKey(terminalKey)) { + PropertyBags equipmentTerminalsDiagramData = terminalsDiagramData.get(terminalKey); + equipmentTerminalsDiagramData.forEach(terminalDiagramData -> + diagramDetails.addTerminalPoint(terminal, new DiagramPoint(terminalDiagramData.asDouble("x"), terminalDiagramData.asDouble("y"), terminalDiagramData.asInt("seq"))) + ); + } else { + LOG.warn("Cannot find terminal diagram data of equipment {}, name {}, terminal {}", equipmentId, equipmentName, terminal); + } + } +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/AbstractInjectionDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/AbstractInjectionDiagramDataImporter.java new file mode 100644 index 000000000..dadfb1a42 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/AbstractInjectionDiagramDataImporter.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Map; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractInjectionDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractInjectionDiagramDataImporter.class); + + protected Network network; + protected Map terminalsDiagramData; + + public AbstractInjectionDiagramDataImporter(Network network, Map terminalsDiagramData) { + this.network = Objects.requireNonNull(network); + this.terminalsDiagramData = Objects.requireNonNull(terminalsDiagramData); + } + + protected void addTerminalPoints(String equipmentId, String equipmentName, InjectionDiagramData.InjectionDiagramDetails diagramDetails) { + String terminalKey = equipmentId + "_1"; + if (terminalsDiagramData.containsKey(terminalKey)) { + PropertyBags equipmentTerminalsDiagramData = terminalsDiagramData.get(terminalKey); + equipmentTerminalsDiagramData.forEach(terminalDiagramData -> + diagramDetails.addTerminalPoint(new DiagramPoint(terminalDiagramData.asDouble("x"), terminalDiagramData.asDouble("y"), terminalDiagramData.asInt("seq"))) + ); + } else { + LOG.warn("Cannot find terminal diagram data of equipment {}, name {}", equipmentId, equipmentName); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/BusDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/BusDiagramDataImporter.java new file mode 100644 index 000000000..b7f46bfd8 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/BusDiagramDataImporter.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Objects; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.triplestore.api.PropertyBag; + +/** + * + * @author Massimo Ferraro + */ +public class BusDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(BusDiagramDataImporter.class); + + private Network network; + + public BusDiagramDataImporter(Network network) { + this.network = Objects.requireNonNull(network); + } + + public void importDiagramData(PropertyBag busDiagramData) { + Objects.requireNonNull(busDiagramData); + String busId = busDiagramData.getId("identifiedObject"); + String vlId = busDiagramData.getId("voltageLevel"); + VoltageLevel vl = network.getVoltageLevel(vlId); + if (vl != null) { + Bus bus = vl.getBusBreakerView().getBus(busId); + if (bus != null) { + NodeDiagramData busIidmDiagramData = bus.getExtension(NodeDiagramData.class); + if (busIidmDiagramData == null) { + busIidmDiagramData = new NodeDiagramData<>(bus); + } + String diagramName = busDiagramData.get("diagramName"); + NodeDiagramData.NodeDiagramDataDetails diagramDetails = busIidmDiagramData.getData(diagramName); + if (diagramDetails == null) { + diagramDetails = busIidmDiagramData.new NodeDiagramDataDetails(); + } + if (busDiagramData.asInt("seq") == 1) { + diagramDetails.setPoint1(new DiagramPoint(busDiagramData.asDouble("x"), busDiagramData.asDouble("y"), busDiagramData.asInt("seq"))); + } else { + diagramDetails.setPoint2(new DiagramPoint(busDiagramData.asDouble("x"), busDiagramData.asDouble("y"), busDiagramData.asInt("seq"))); + } + busIidmDiagramData.addData(diagramName, diagramDetails); + bus.addExtension(NodeDiagramData.class, busIidmDiagramData); + NetworkDiagramData.addDiagramName(network, diagramName); + } else { + LOG.warn("Cannot find bus {}, name {} in network {}: skipping bus diagram data", busId, busDiagramData.get("name"), network.getId()); + } + } else { + LOG.warn("Cannot find voltage level {}, name {} in network {}: skipping bus diagram data", vlId, busDiagramData.get("vlname"), network.getId()); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/BusbarDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/BusbarDiagramDataImporter.java new file mode 100644 index 000000000..76867f928 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/BusbarDiagramDataImporter.java @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Objects; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBag; + +/** + * + * @author Massimo Ferraro + */ +public class BusbarDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(BusbarDiagramDataImporter.class); + + private Network network; + + public BusbarDiagramDataImporter(Network network) { + this.network = Objects.requireNonNull(network); + } + + public void importDiagramData(PropertyBag busbarDiagramData) { + Objects.requireNonNull(busbarDiagramData); + String busbarId = busbarDiagramData.getId("busbarSection"); + BusbarSection busbar = network.getBusbarSection(busbarId); + if (busbar != null) { + NodeDiagramData busbarIidmDiagramData = busbar.getExtension(NodeDiagramData.class); + if (busbarIidmDiagramData == null) { + busbarIidmDiagramData = new NodeDiagramData<>(busbar); + } + String diagramName = busbarDiagramData.get("diagramName"); + NodeDiagramData.NodeDiagramDataDetails diagramDetails = busbarIidmDiagramData.getData(diagramName); + if (diagramDetails == null) { + diagramDetails = busbarIidmDiagramData.new NodeDiagramDataDetails(); + } + if (busbarDiagramData.asInt("seq") == 1) { + diagramDetails.setPoint1(new DiagramPoint(busbarDiagramData.asDouble("x"), busbarDiagramData.asDouble("y"), busbarDiagramData.asInt("seq"))); + } else { + diagramDetails.setPoint2(new DiagramPoint(busbarDiagramData.asDouble("x"), busbarDiagramData.asDouble("y"), busbarDiagramData.asInt("seq"))); + } + busbarIidmDiagramData.addData(diagramName, diagramDetails); + busbar.addExtension(NodeDiagramData.class, busbarIidmDiagramData); + NetworkDiagramData.addDiagramName(network, diagramName); + } else { + LOG.warn("Cannot find busbar {}, name {} in network {}: skipping busbar diagram data", busbarId, busbarDiagramData.get("name"), network.getId()); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/GeneratorDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/GeneratorDiagramDataImporter.java new file mode 100644 index 000000000..33e03729e --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/GeneratorDiagramDataImporter.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class GeneratorDiagramDataImporter extends AbstractInjectionDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(GeneratorDiagramDataImporter.class); + + public GeneratorDiagramDataImporter(Network network, Map terminalsDiagramData) { + super(network, terminalsDiagramData); + } + + public void importDiagramData(PropertyBag generatorDiagramData) { + Objects.requireNonNull(generatorDiagramData); + String generatorId = generatorDiagramData.getId("identifiedObject"); + Generator generator = network.getGenerator(generatorId); + if (generator != null) { + InjectionDiagramData generatorIidmDiagramData = new InjectionDiagramData<>(generator); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = generatorIidmDiagramData.new InjectionDiagramDetails(new DiagramPoint(generatorDiagramData.asDouble("x"), generatorDiagramData.asDouble("y"), generatorDiagramData.asInt("seq")), + generatorDiagramData.asDouble("rotation")); + addTerminalPoints(generatorId, generator.getName(), diagramDetails); + String diagramName = generatorDiagramData.get("diagramName"); + generatorIidmDiagramData.addData(diagramName, diagramDetails); + generator.addExtension(InjectionDiagramData.class, generatorIidmDiagramData); + NetworkDiagramData.addDiagramName(network, diagramName); + } else { + LOG.warn("Cannot find generator {}, name {} in network {}: skipping generator diagram data", generatorId, generatorDiagramData.get("name"), network.getId()); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/HvdcLineDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/HvdcLineDiagramDataImporter.java new file mode 100644 index 000000000..9ef8bd950 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/HvdcLineDiagramDataImporter.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Objects; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBag; + +/** + * + * @author Massimo Ferraro + */ +public class HvdcLineDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(HvdcLineDiagramDataImporter.class); + + private Network network; + + public HvdcLineDiagramDataImporter(Network network) { + this.network = Objects.requireNonNull(network); + } + + public void importDiagramData(PropertyBag hvdcLineDiagramData) { + Objects.requireNonNull(hvdcLineDiagramData); + String hvdcLineId = hvdcLineDiagramData.getId("identifiedObject"); + HvdcLine hvdcLine = network.getHvdcLine(hvdcLineId); + if (hvdcLine != null) { + LineDiagramData hvdcLineIidmDiagramData = hvdcLine.getExtension(LineDiagramData.class); + if (hvdcLineIidmDiagramData == null) { + hvdcLineIidmDiagramData = new LineDiagramData<>(hvdcLine); + } + String diagramName = hvdcLineDiagramData.get("diagramName"); + hvdcLineIidmDiagramData.addPoint(diagramName, new DiagramPoint(hvdcLineDiagramData.asDouble("x"), hvdcLineDiagramData.asDouble("y"), hvdcLineDiagramData.asInt("seq"))); + hvdcLine.addExtension(LineDiagramData.class, hvdcLineIidmDiagramData); + NetworkDiagramData.addDiagramName(network, diagramName); + } else { + LOG.warn("Cannot find HVDC line {}, name {} in network {}: skipping HVDC line diagram data", hvdcLineId, hvdcLineDiagramData.get("name"), network.getId()); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/LineDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/LineDiagramDataImporter.java new file mode 100644 index 000000000..03e460f1e --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/LineDiagramDataImporter.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.CgmesDLModel; +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBag; + +/** + * + * @author Massimo Ferraro + */ +public class LineDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(LineDiagramDataImporter.class); + + private Network network; + + public LineDiagramDataImporter(Network network) { + this.network = Objects.requireNonNull(network); + } + + public void importDiagramData(PropertyBag lineDiagramData) { + Objects.requireNonNull(lineDiagramData); + String lineId = lineDiagramData.getId("identifiedObject"); + Line line = network.getLine(lineId); + if (line != null) { + LineDiagramData lineIidmDiagramData = line.getExtension(LineDiagramData.class); + if (lineIidmDiagramData == null) { + lineIidmDiagramData = new LineDiagramData<>(line); + } + lineIidmDiagramData.addPoint(lineDiagramData.get(CgmesDLModel.DIAGRAM_NAME), new DiagramPoint(lineDiagramData.asDouble("x"), lineDiagramData.asDouble("y"), lineDiagramData.asInt("seq"))); + line.addExtension(LineDiagramData.class, lineIidmDiagramData); + NetworkDiagramData.addDiagramName(network, lineDiagramData.get(CgmesDLModel.DIAGRAM_NAME)); + } else { + DanglingLine danglingLine = network.getDanglingLine(lineId); + if (danglingLine != null) { + LineDiagramData danglingLineDiagramData = danglingLine.getExtension(LineDiagramData.class); + if (danglingLineDiagramData == null) { + danglingLineDiagramData = new LineDiagramData<>(danglingLine); + } + + danglingLineDiagramData.addPoint(lineDiagramData.get(CgmesDLModel.DIAGRAM_NAME), new DiagramPoint(lineDiagramData.asDouble("x"), lineDiagramData.asDouble("y"), lineDiagramData.asInt("seq"))); + danglingLine.addExtension(LineDiagramData.class, danglingLineDiagramData); + NetworkDiagramData.addDiagramName(network, lineDiagramData.get(CgmesDLModel.DIAGRAM_NAME)); + } else { + LOG.warn("Cannot find line/dangling line {}, name {} in network {}: skipping line diagram data", lineId, lineDiagramData.get("name"), network.getId()); + } + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/LoadDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/LoadDiagramDataImporter.java new file mode 100644 index 000000000..37ef42fc4 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/LoadDiagramDataImporter.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class LoadDiagramDataImporter extends AbstractInjectionDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(LoadDiagramDataImporter.class); + + public LoadDiagramDataImporter(Network network, Map terminalsDiagramData) { + super(network, terminalsDiagramData); + } + + public void importDiagramData(PropertyBag loadDiagramData) { + Objects.requireNonNull(loadDiagramData); + String loadId = loadDiagramData.getId("identifiedObject"); + Load load = network.getLoad(loadId); + if (load != null) { + InjectionDiagramData loadIidmDiagramData = new InjectionDiagramData<>(load); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = loadIidmDiagramData.new InjectionDiagramDetails(new DiagramPoint(loadDiagramData.asDouble("x"), loadDiagramData.asDouble("y"), loadDiagramData.asInt("seq")), + loadDiagramData.asDouble("rotation")); + addTerminalPoints(loadId, load.getName(), diagramDetails); + loadIidmDiagramData.addData(loadDiagramData.get("diagramName"), diagramDetails); + load.addExtension(InjectionDiagramData.class, loadIidmDiagramData); + NetworkDiagramData.addDiagramName(network, loadDiagramData.get("diagramName")); + } else { + LOG.warn("Cannot find load {}, name {} in network {}: skipping load diagram data", loadId, loadDiagramData.get("name"), network.getId()); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/ShuntDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/ShuntDiagramDataImporter.java new file mode 100644 index 000000000..6b8ef4c9e --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/ShuntDiagramDataImporter.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class ShuntDiagramDataImporter extends AbstractInjectionDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(ShuntDiagramDataImporter.class); + + public ShuntDiagramDataImporter(Network network, Map terminalsDiagramData) { + super(network, terminalsDiagramData); + } + + public void importDiagramData(PropertyBag shuntDiagramData) { + Objects.requireNonNull(shuntDiagramData); + String shuntId = shuntDiagramData.getId("identifiedObject"); + ShuntCompensator shunt = network.getShuntCompensator(shuntId); + if (shunt != null) { + InjectionDiagramData shuntIidmDiagramData = new InjectionDiagramData<>(shunt); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = shuntIidmDiagramData.new InjectionDiagramDetails(new DiagramPoint(shuntDiagramData.asDouble("x"), shuntDiagramData.asDouble("y"), shuntDiagramData.asInt("seq")), + shuntDiagramData.asDouble("rotation")); + addTerminalPoints(shuntId, shunt.getName(), diagramDetails); + shuntIidmDiagramData.addData(shuntDiagramData.get("diagramName"), diagramDetails); + shunt.addExtension(InjectionDiagramData.class, shuntIidmDiagramData); + NetworkDiagramData.addDiagramName(network, shuntDiagramData.get("diagramName")); + } else { + LOG.warn("Cannot find shunt {}, name {} in network {}: skipping shunt diagram data", shuntId, shuntDiagramData.get("name"), network.getId()); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/SvcDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/SvcDiagramDataImporter.java new file mode 100644 index 000000000..03105a310 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/SvcDiagramDataImporter.java @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.StaticVarCompensator; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class SvcDiagramDataImporter extends AbstractInjectionDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(SvcDiagramDataImporter.class); + + public SvcDiagramDataImporter(Network network, Map terminalsDiagramData) { + super(network, terminalsDiagramData); + } + + public void importDiagramData(PropertyBag svcDiagramData) { + Objects.requireNonNull(svcDiagramData); + String svcId = svcDiagramData.getId("identifiedObject"); + StaticVarCompensator svc = network.getStaticVarCompensator(svcId); + if (svc != null) { + InjectionDiagramData svcIidmDiagramData = new InjectionDiagramData<>(svc); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = svcIidmDiagramData.new InjectionDiagramDetails(new DiagramPoint(svcDiagramData.asDouble("x"), svcDiagramData.asDouble("y"), svcDiagramData.asInt("seq")), + svcDiagramData.asDouble("rotation")); + addTerminalPoints(svcId, svc.getName(), diagramDetails); + svcIidmDiagramData.addData(svcDiagramData.get("diagramName"), diagramDetails); + svc.addExtension(InjectionDiagramData.class, svcIidmDiagramData); + NetworkDiagramData.addDiagramName(network, svcDiagramData.get("diagramName")); + } else { + LOG.warn("Cannot find svc {}, name {} in network {}: skipping svc diagram data", svcId, svcDiagramData.get("name"), network.getId()); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/SwitchDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/SwitchDiagramDataImporter.java new file mode 100644 index 000000000..7a4692c22 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/SwitchDiagramDataImporter.java @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.CouplingDeviceDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramTerminal; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Switch; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * @author Massimo Ferraro + */ +public class SwitchDiagramDataImporter extends AbstractCouplingDeviceDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(SwitchDiagramDataImporter.class); + + public SwitchDiagramDataImporter(Network network, Map terminalsDiagramData) { + super(network, terminalsDiagramData); + } + + public void importDiagramData(PropertyBag switchesDiagramData) { + Objects.requireNonNull(switchesDiagramData); + String switchId = switchesDiagramData.getId("identifiedObject"); + Switch sw = network.getSwitch(switchId); + if (sw != null) { + CouplingDeviceDiagramData switchIidmDiagramData = new CouplingDeviceDiagramData<>(sw); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails diagramDetails = switchIidmDiagramData.new CouplingDeviceDiagramDetails(new DiagramPoint(switchesDiagramData.asDouble("x"), switchesDiagramData.asDouble("y"), 0), + switchesDiagramData.asDouble("rotation")); + addTerminalPoints(switchId, sw.getName(), DiagramTerminal.TERMINAL1, "1", diagramDetails); + addTerminalPoints(switchId, sw.getName(), DiagramTerminal.TERMINAL2, "2", diagramDetails); + switchIidmDiagramData.addData(switchesDiagramData.get("diagramName"), diagramDetails); + sw.addExtension(CouplingDeviceDiagramData.class, switchIidmDiagramData); + NetworkDiagramData.addDiagramName(network, switchesDiagramData.get("diagramName")); + } else { + LOG.warn("Cannot find switch {}, name {} in network {}: skipping switch diagram data", switchId, switchesDiagramData.get("name"), network.getId()); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/TransformerDiagramDataImporter.java b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/TransformerDiagramDataImporter.java new file mode 100644 index 000000000..30dfa45ad --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/java/com/powsybl/cgmes/dl/conversion/importers/TransformerDiagramDataImporter.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.importers; + +import java.util.Map; +import java.util.Objects; + +import com.powsybl.cgmes.dl.conversion.CgmesDLModel; +import com.powsybl.cgmes.iidm.extensions.dl.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class TransformerDiagramDataImporter extends AbstractCouplingDeviceDiagramDataImporter { + + private static final Logger LOG = LoggerFactory.getLogger(TransformerDiagramDataImporter.class); + + public TransformerDiagramDataImporter(Network network, Map terminalsDiagramData) { + super(network, terminalsDiagramData); + } + + public void importDiagramData(PropertyBag transformersDiagramData) { + Objects.requireNonNull(transformersDiagramData); + String transformerId = transformersDiagramData.getId("identifiedObject"); + TwoWindingsTransformer transformer = network.getTwoWindingsTransformer(transformerId); + if (transformer != null) { + CouplingDeviceDiagramData transformerIidmDiagramData = new CouplingDeviceDiagramData<>(transformer); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails diagramDetails = transformerIidmDiagramData.new CouplingDeviceDiagramDetails(new DiagramPoint(transformersDiagramData.asDouble("x"), transformersDiagramData.asDouble("y"), transformersDiagramData.asInt("seq")), + transformersDiagramData.asDouble("rotation")); + addTerminalPoints(transformerId, transformer.getName(), DiagramTerminal.TERMINAL1, "1", diagramDetails); + addTerminalPoints(transformerId, transformer.getName(), DiagramTerminal.TERMINAL2, "2", diagramDetails); + transformerIidmDiagramData.addData(transformersDiagramData.get(CgmesDLModel.DIAGRAM_NAME), diagramDetails); + transformer.addExtension(CouplingDeviceDiagramData.class, transformerIidmDiagramData); + NetworkDiagramData.addDiagramName(network, transformersDiagramData.get(CgmesDLModel.DIAGRAM_NAME)); + } else { + ThreeWindingsTransformer transformer3w = network.getThreeWindingsTransformer(transformerId); + if (transformer3w != null) { + ThreeWindingsTransformerDiagramData transformerIidmDiagramData = new ThreeWindingsTransformerDiagramData(transformer3w); + ThreeWindingsTransformerDiagramData.ThreeWindingsTransformerDiagramDataDetails diagramDetails = transformerIidmDiagramData.new ThreeWindingsTransformerDiagramDataDetails(new DiagramPoint(transformersDiagramData.asDouble("x"), transformersDiagramData.asDouble("y"), transformersDiagramData.asInt("seq")), + transformersDiagramData.asDouble("rotation")); + addTerminalPoints(transformerId, transformer3w.getName(), DiagramTerminal.TERMINAL1, "1", diagramDetails); + addTerminalPoints(transformerId, transformer3w.getName(), DiagramTerminal.TERMINAL2, "2", diagramDetails); + addTerminalPoints(transformerId, transformer3w.getName(), DiagramTerminal.TERMINAL3, "3", diagramDetails); + transformerIidmDiagramData.addData(transformersDiagramData.get(CgmesDLModel.DIAGRAM_NAME), diagramDetails); + transformer3w.addExtension(ThreeWindingsTransformerDiagramData.class, transformerIidmDiagramData); + NetworkDiagramData.addDiagramName(network, transformersDiagramData.get(CgmesDLModel.DIAGRAM_NAME)); + } else { + LOG.warn("Cannot find transformer {}, name {} in network {}: skipping transformer diagram data", transformerId, transformersDiagramData.get("name"), network.getId()); + } + } + } + + private void addTerminalPoints(String transformerId, String transformerName, DiagramTerminal terminal, String terminalSide, ThreeWindingsTransformerDiagramData.ThreeWindingsTransformerDiagramDataDetails diagramDetails) { + String terminalKey = transformerId + "_" + terminalSide; + if (terminalsDiagramData.containsKey(terminalKey)) { + PropertyBags equipmentTerminalsDiagramData = terminalsDiagramData.get(terminalKey); + equipmentTerminalsDiagramData.forEach(terminalDiagramData -> + diagramDetails.addTerminalPoint(terminal, new DiagramPoint(terminalDiagramData.asDouble("x"), terminalDiagramData.asDouble("y"), terminalDiagramData.asInt("seq"))) + ); + } else { + LOG.warn("Cannot find terminal diagram data of transformer {}, name {}, terminal {}", transformerId, transformerName, terminal); + } + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/main/resources/CGMES-DL.sparql b/cgmes-dl/cgmes-dl-conversion/src/main/resources/CGMES-DL.sparql new file mode 100644 index 000000000..93d125915 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/main/resources/CGMES-DL.sparql @@ -0,0 +1,281 @@ +# +# Copyright (c) 2019, RTE (http://www.rte-france.com) +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +# query: terminalDiagramData +SELECT ?terminalEquipment ?terminalSide ?x ?y ?seq ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a cim:Terminal ; + cim:ACDCTerminal.sequenceNumber ?terminalSide ; + cim:Terminal.ConductingEquipment ?terminalEquipment . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + + +# query: busDiagramData +SELECT ?identifiedObject ?name ?voltageLevel ?vlname ?x ?y ?seq ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a cim:TopologicalNode ; + cim:IdentifiedObject.name ?name ; + cim:TopologicalNode.ConnectivityNodeContainer ?voltageLevel . + ?voltageLevel + a cim:VoltageLevel ; + cim:IdentifiedObject.name ?vlname . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: busbarDiagramData +SELECT ?busbarSection ?name ?x ?y ?seq ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a cim:ConnectivityNode . + ?terminal + a cim:Terminal ; + cim:Terminal.ConnectivityNode ?identifiedObject ; + cim:Terminal.ConductingEquipment ?busbarSection . + ?busbarSection + a cim:BusbarSection ; + cim:IdentifiedObject.name ?name . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: lineDiagramData +SELECT ?identifiedObject ?name ?x ?y ?seq ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a ?type ; + cim:IdentifiedObject.name ?name . + VALUES ?type { cim:ACLineSegment cim:SeriesCompensator } . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: generatorDiagramData +SELECT ?identifiedObject ?name ?x ?y ?seq ?rotation ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.rotation ?rotation ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a ?type ; + cim:IdentifiedObject.name ?name . + VALUES ?type { cim:SynchronousMachine cim:ExternalNetworkInjection cim:EquivalentInjection } . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: loadDiagramData +SELECT ?identifiedObject ?name ?x ?y ?seq ?rotation ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.rotation ?rotation ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a ?type ; + cim:IdentifiedObject.name ?name . + VALUES ?type { cim:EnergyConsumer cim:AsynchronousMachine cim:ConformLoad cim:NonConformLoad } . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: shuntDiagramData +SELECT ?identifiedObject ?name ?x ?y ?seq ?rotation ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.rotation ?rotation ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a ?type ; + cim:IdentifiedObject.name ?name . + VALUES ?type { cim:LinearShuntCompensator cim:NonlinearShuntCompensator } . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: svcDiagramData +SELECT ?identifiedObject ?name ?x ?y ?seq ?rotation ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.rotation ?rotation ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a cim:StaticVarCompensator ; + cim:IdentifiedObject.name ?name . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: switchDiagramData +SELECT ?identifiedObject ?name ?x ?y ?rotation ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.rotation ?rotation ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a ?type ; + cim:IdentifiedObject.name ?name . + VALUES ?type { cim:Breaker cim:Disconnector } . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: transformerDiagramData +SELECT ?identifiedObject ?name ?x ?y ?seq ?rotation ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.rotation ?rotation ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a cim:PowerTransformer ; + cim:IdentifiedObject.name ?name . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: hvdcLineDiagramData +SELECT ?identifiedObject ?name ?x ?y ?seq ?diagramName +{ + ?diagramObject + a cim:DiagramObject ; + cim:DiagramObject.IdentifiedObject ?identifiedObject ; + cim:DiagramObject.Diagram ?diagram . + ?identifiedObject + a cim:DCLineSegment ; + cim:IdentifiedObject.name ?name . + ?diagramObjectPoint + a cim:DiagramObjectPoint ; + cim:DiagramObjectPoint.DiagramObject ?diagramObject ; + cim:DiagramObjectPoint.sequenceNumber ?seq ; + cim:DiagramObjectPoint.xPosition ?x ; + cim:DiagramObjectPoint.yPosition ?y . + ?diagram + a cim:Diagram ; + cim:IdentifiedObject.name ?diagramName . +} + +# query: terminals +SELECT ?terminal ?terminalSide ?equipment +{ + ?terminal + a cim:Terminal ; + cim:ACDCTerminal.sequenceNumber ?terminalSide ; + cim:Terminal.ConductingEquipment ?equipment . +} + +# query: busbarNodes +SELECT ?busbarNode ?busbarSection +{ + ?busbarNode + a cim:ConnectivityNode . + ?terminal + a cim:Terminal ; + cim:Terminal.ConnectivityNode ?busbarNode ; + cim:Terminal.ConductingEquipment ?busbarSection . + ?busbarSection + a cim:BusbarSection . +} \ No newline at end of file diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/AbstractCgmesDLExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/AbstractCgmesDLExporterTest.java new file mode 100644 index 000000000..79cbd6fca --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/AbstractCgmesDLExporterTest.java @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Matchers; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.model.CgmesNamespace; +import com.powsybl.cgmes.model.CgmesSubset; +import com.powsybl.commons.datasource.DataSource; +import com.powsybl.iidm.network.Network; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractCgmesDLExporterTest { + + protected final String basename = "network"; + protected final String dataNs = "http://" + basename + "/#"; + protected final String diagramId = "diagramId"; + protected final String diagramObjectStyleId = "diagramObjectStyleId"; + protected final String diagramObjectId = "diagramObjectId"; + + protected Network network; + protected TripleStore tripleStore; + protected CgmesDLModel cgmesDLModel; + protected DataSource dataSource; + + protected ArgumentCaptor prefixCaptor; + protected ArgumentCaptor namespaceCaptor; + protected ArgumentCaptor contextCaptor; + protected ArgumentCaptor nsCaptor; + protected ArgumentCaptor typeCaptor; + protected ArgumentCaptor propertiesCaptor; + + @Before + public void setUp() { + tripleStore = Mockito.mock(TripleStore.class); + Mockito.when(tripleStore.add(Matchers.any(String.class), Matchers.eq(CgmesNamespace.CIM_16_NAMESPACE), + Matchers.eq("Diagram"), Matchers.any(PropertyBag.class))) + .thenReturn(diagramId); + Mockito.when(tripleStore.add(Matchers.any(String.class), Matchers.eq(CgmesNamespace.CIM_16_NAMESPACE), + Matchers.eq("DiagramObjectStyle"), Matchers.any(PropertyBag.class))) + .thenReturn(diagramObjectStyleId); + Mockito.when(tripleStore.add(Matchers.any(String.class), Matchers.eq(CgmesNamespace.CIM_16_NAMESPACE), + Matchers.eq("DiagramObject"), Matchers.any(PropertyBag.class))) + .thenReturn(diagramObjectId); + + cgmesDLModel = Mockito.mock(CgmesDLModel.class); + + dataSource = Mockito.mock(DataSource.class); + Mockito.when(dataSource.getBaseName()).thenReturn(basename); + + prefixCaptor = ArgumentCaptor.forClass(String.class); + namespaceCaptor = ArgumentCaptor.forClass(String.class); + contextCaptor = ArgumentCaptor.forClass(String.class); + nsCaptor = ArgumentCaptor.forClass(String.class); + typeCaptor = ArgumentCaptor.forClass(String.class); + propertiesCaptor = ArgumentCaptor.forClass(PropertyBag.class); + } + + protected PropertyBag getTerminal(String equipmentId, String terminalId, int side) { + PropertyBag terminal = new PropertyBag(Arrays.asList("equipment", "terminalSide", "terminal")); + terminal.put("equipment", dataNs + equipmentId); + terminal.put("terminal", dataNs + terminalId); + terminal.put("terminalSide", Integer.toString(side)); + return terminal; + } + + @Test + public void test() { + new CgmesDLExporter(network, tripleStore, cgmesDLModel).exportDLData(dataSource); + checkNamespaces(); + checkStatements(); + } + + protected void checkNamespaces() { + Mockito.verify(tripleStore, Mockito.times(3)).addNamespace(prefixCaptor.capture(), namespaceCaptor.capture()); + assertEquals("data", prefixCaptor.getAllValues().get(0)); + assertEquals(dataNs, namespaceCaptor.getAllValues().get(0)); + assertEquals("cim", prefixCaptor.getAllValues().get(1)); + assertEquals(CgmesNamespace.CIM_16_NAMESPACE, namespaceCaptor.getAllValues().get(1)); + assertEquals("md", prefixCaptor.getAllValues().get(2)); + assertEquals(CgmesDLExporter.MD_NAMESPACE, namespaceCaptor.getAllValues().get(2)); + } + + protected abstract void checkStatements(); + + protected void checkProperties(String context, String namespace, String type, PropertyBag properties, String expectedType, + List expectedProperties, List expectedResources, List expectedClassProperties) { + assertTrue(CgmesSubset.DIAGRAM_LAYOUT.isValidName(context)); + assertEquals(basename + "_" + CgmesSubset.DIAGRAM_LAYOUT.getIdentifier() + ".xml", context); + assertEquals(CgmesNamespace.CIM_16_NAMESPACE, namespace); + assertEquals(expectedType, type); + assertEquals(expectedProperties.size(), properties.propertyNames().size()); + expectedProperties.forEach(property -> assertTrue(properties.propertyNames().contains(property))); + expectedResources.forEach(resource -> assertTrue(properties.isResource(resource))); + expectedClassProperties.forEach(classProperty -> assertTrue(properties.isClassProperty(classProperty))); + } + + protected void checkDiagram(String context, String namespace, String type, PropertyBag properties) { + checkProperties(context, namespace, type, properties, "Diagram", Arrays.asList("IdentifiedObject.name", "orientation"), + Arrays.asList("orientation"), Arrays.asList("IdentifiedObject.name")); + assertEquals(basename, properties.get("IdentifiedObject.name")); + assertEquals(CgmesNamespace.CIM_16_NAMESPACE + "OrientationKind.negative", properties.get("orientation")); + } + + protected void checkDiagramObjectStyle(String context, String namespace, String type, PropertyBag properties, String expectedName) { + checkProperties(context, namespace, type, properties, "DiagramObjectStyle", Arrays.asList("IdentifiedObject.name"), + Collections.emptyList(), Arrays.asList("IdentifiedObject.name")); + assertEquals(expectedName, properties.get("IdentifiedObject.name")); + } + + protected void checkDiagramObject(String context, String namespace, String type, PropertyBag properties, + String expectedName, String expectedId, double expectedRotation) { + checkProperties(context, namespace, type, properties, "DiagramObject", + Arrays.asList("IdentifiedObject.name", "IdentifiedObject", "rotation", "Diagram", "DiagramObjectStyle"), + Arrays.asList("IdentifiedObject", "Diagram", "DiagramObjectStyle"), Arrays.asList("IdentifiedObject.name")); + assertEquals(expectedName, properties.get("IdentifiedObject.name")); + assertEquals(expectedId, properties.get("IdentifiedObject")); + assertEquals(expectedRotation, properties.asDouble("rotation"), 0); + assertEquals(diagramId, properties.get("Diagram")); + assertEquals(diagramObjectStyleId, properties.get("DiagramObjectStyle")); + } + + protected void checkDiagramObjectPoint(String context, String namespace, String type, PropertyBag properties, DiagramPoint expectedPoint) { + checkProperties(context, namespace, type, properties, "DiagramObjectPoint", + Arrays.asList("DiagramObject", "sequenceNumber", "xPosition", "yPosition"), + Arrays.asList("DiagramObject"), Collections.emptyList()); + assertEquals(diagramObjectId, properties.get("DiagramObject")); + assertEquals(expectedPoint.getSeq(), properties.asInt("sequenceNumber")); + assertEquals(expectedPoint.getX(), properties.asDouble("xPosition"), 0); + assertEquals(expectedPoint.getY(), properties.asDouble("yPosition"), 0); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/AbstractCgmesDLTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/AbstractCgmesDLTest.java new file mode 100644 index 000000000..4d0abb96b --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/AbstractCgmesDLTest.java @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import java.util.Arrays; + +import org.junit.Before; + +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractCgmesDLTest { + + protected static String NAMESPACE = "http://network#"; + + protected static String DEFAULT_DIAGRAM_NAME = "default"; + + protected PropertyBags terminalsPropertyBags; + protected PropertyBags busesPropertyBags; + protected PropertyBags busbarsPropertyBags; + protected PropertyBags linesPropertyBags; + protected PropertyBags danglingLinesPropertyBags; + protected PropertyBags generatorsPropertyBags; + protected PropertyBags loadsPropertyBags; + protected PropertyBags shuntsPropertyBags; + protected PropertyBags switchesPropertyBags; + protected PropertyBags tranformersPropertyBags; + protected PropertyBags tranformers3wPropertyBags; + protected PropertyBags hvdcLinesPropertyBags; + protected PropertyBags svcsPropertyBags; + protected PropertyBags terminals; + protected PropertyBags busbarNodes; + + @Before + public void setUp() { + terminalsPropertyBags = new PropertyBags(Arrays.asList(createTerminalPropertyBag(NAMESPACE + "Generator", "1", 2, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Generator", "1", 6, 10, 2), + createTerminalPropertyBag(NAMESPACE + "Load", "1", 2, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Load", "1", 6, 10, 2), + createTerminalPropertyBag(NAMESPACE + "Shunt", "1", 2, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Shunt", "1", 6, 10, 2), + createTerminalPropertyBag(NAMESPACE + "Switch", "1", 2, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Switch", "1", 6, 10, 2), + createTerminalPropertyBag(NAMESPACE + "Switch", "2", 14, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Switch", "2", 18, 10, 2), + createTerminalPropertyBag(NAMESPACE + "Transformer", "1", 2, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Transformer", "1", 6, 10, 2), + createTerminalPropertyBag(NAMESPACE + "Transformer", "2", 14, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Transformer", "2", 18, 10, 2), + createTerminalPropertyBag(NAMESPACE + "Transformer3w", "1", 2, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Transformer3w", "1", 6, 10, 2), + createTerminalPropertyBag(NAMESPACE + "Transformer3w", "2", 14, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Transformer3w", "2", 18, 10, 2), + createTerminalPropertyBag(NAMESPACE + "Transformer3w", "3", 10, 16, 1), + createTerminalPropertyBag(NAMESPACE + "Transformer3w", "3", 10, 20, 2), + createTerminalPropertyBag(NAMESPACE + "Svc", "1", 2, 10, 1), + createTerminalPropertyBag(NAMESPACE + "Svc", "1", 6, 10, 2))); + busesPropertyBags = new PropertyBags(Arrays.asList(createBusPropertyBag(NAMESPACE + "Bus", "Bus", NAMESPACE + "VoltageLevel", "VoltageLevel", 20, 5, 1), + createBusPropertyBag(NAMESPACE + "Bus", "Bus", NAMESPACE + "VoltageLevel", "VoltageLevel", 20, 40, 2))); + busbarsPropertyBags = new PropertyBags(Arrays.asList(createBusbarPropertyBag(NAMESPACE + "Busbar", "Busbar", 20, 5, 1), + createBusbarPropertyBag(NAMESPACE + "Busbar", "Busbar", 20, 40, 2))); + linesPropertyBags = new PropertyBags(Arrays.asList(createPropertyBag(NAMESPACE + "Line", "Line", 20, 5, 1), + createPropertyBag(NAMESPACE + "Line", "Line", 20, 40, 2))); + danglingLinesPropertyBags = new PropertyBags(Arrays.asList(createPropertyBag(NAMESPACE + "DanglingLine", "DanglingLine", 20, 5, 1), + createPropertyBag(NAMESPACE + "DanglingLine", "DanglingLine", 20, 40, 2))); + generatorsPropertyBags = new PropertyBags(Arrays.asList(createPropertyBag(NAMESPACE + "Generator", "Generator", 10, 10, 0, 90))); + loadsPropertyBags = new PropertyBags(Arrays.asList(createPropertyBag(NAMESPACE + "Load", "Load", 10, 10, 0, 90))); + shuntsPropertyBags = new PropertyBags(Arrays.asList(createPropertyBag(NAMESPACE + "Shunt", "Shunt", 10, 10, 0, 90))); + switchesPropertyBags = new PropertyBags(Arrays.asList(createSwitchPropertyBag(NAMESPACE + "Switch", "Switch", 10, 10, 90))); + tranformersPropertyBags = new PropertyBags(Arrays.asList(createPropertyBag(NAMESPACE + "Transformer", "Transformer", 10, 10, 0, 90))); + tranformers3wPropertyBags = new PropertyBags(Arrays.asList(createPropertyBag(NAMESPACE + "Transformer3w", "Transformer3w", 10, 13, 0, 90))); + hvdcLinesPropertyBags = new PropertyBags(Arrays.asList(createPropertyBag(NAMESPACE + "HvdcLine", "HvdcLine", 20, 5, 1), + createPropertyBag(NAMESPACE + "HvdcLine", "HvdcLine", 20, 40, 2))); + svcsPropertyBags = new PropertyBags(Arrays.asList(createPropertyBag(NAMESPACE + "Svc", "Svc", 10, 10, 0, 90))); + terminals = new PropertyBags(Arrays.asList(createTerminal(NAMESPACE + "Generator_0", 1, NAMESPACE + "Generator"), + createTerminal(NAMESPACE + "Load_0", 1, NAMESPACE + "Load"), + createTerminal(NAMESPACE + "Shunt_0", 1, NAMESPACE + "Shunt"), + createTerminal(NAMESPACE + "Svc_0", 1, NAMESPACE + "Svc"), + createTerminal(NAMESPACE + "Line_0", 1, NAMESPACE + "Line"), + createTerminal(NAMESPACE + "Line_1", 2, NAMESPACE + "Line"), + createTerminal(NAMESPACE + "DanglingLine_0", 1, NAMESPACE + "DanglingLine"), + createTerminal(NAMESPACE + "DanglingLine_1", 2, NAMESPACE + "DanglingLine"), + createTerminal(NAMESPACE + "Switch_0", 1, NAMESPACE + "Switch"), + createTerminal(NAMESPACE + "Switch_1", 2, NAMESPACE + "Switch"), + createTerminal(NAMESPACE + "Transformer_0", 1, NAMESPACE + "Transformer"), + createTerminal(NAMESPACE + "Transformer_1", 2, NAMESPACE + "Transformer"), + createTerminal(NAMESPACE + "Transformer3w_0", 1, NAMESPACE + "Transformer3w"), + createTerminal(NAMESPACE + "Transformer3w_1", 2, NAMESPACE + "Transformer3w"), + createTerminal(NAMESPACE + "Transformer3w_0", 3, NAMESPACE + "Transformer3w"), + createTerminal(NAMESPACE + "HvdcLine_0", 1, NAMESPACE + "HvdcLine"), + createTerminal(NAMESPACE + "HvdcLine_1", 2, NAMESPACE + "HvdcLine"))); + busbarNodes = new PropertyBags(Arrays.asList(createBusbarNode(NAMESPACE + "Busbar", NAMESPACE + "BusbarNode"))); + } + + protected PropertyBag createPropertyBag(String identifiedObject, String name, double x, double y, int seq, String diagramName) { + PropertyBag propertyBag = new PropertyBag(Arrays.asList("identifiedObject", "name", "x", "y", "seq")); + propertyBag.put("identifiedObject", identifiedObject); + propertyBag.put("name", name); + propertyBag.put("x", Double.toString(x)); + propertyBag.put("y", Double.toString(y)); + propertyBag.put("seq", Integer.toString(seq)); + propertyBag.put("diagramName", diagramName); + return propertyBag; + } + + protected PropertyBag createPropertyBag(String identifiedObject, String name, double x, double y, int seq) { + return createPropertyBag(identifiedObject, name, x, y, seq, DEFAULT_DIAGRAM_NAME); + } + + protected PropertyBag createPropertyBag(String identifiedObject, String name, double x, double y, int seq, int rotation, String diagramName) { + PropertyBag propertyBag = new PropertyBag(Arrays.asList("identifiedObject", "name", "x", "y", "seq", "rotation")); + propertyBag.put("identifiedObject", identifiedObject); + propertyBag.put("name", name); + propertyBag.put("x", Double.toString(x)); + propertyBag.put("y", Double.toString(y)); + propertyBag.put("seq", Integer.toString(seq)); + propertyBag.put("rotation", Integer.toString(rotation)); + propertyBag.put("diagramName", diagramName); + return propertyBag; + } + + protected PropertyBag createPropertyBag(String identifiedObject, String name, double x, double y, int seq, int rotation) { + return createPropertyBag(identifiedObject, name, x, y, seq, rotation, DEFAULT_DIAGRAM_NAME); + } + + protected PropertyBag createTerminalPropertyBag(String terminalEquipment, String terminalSide, double x, double y, int seq, String diagramName) { + PropertyBag propertyBag = new PropertyBag(Arrays.asList("terminalEquipment", "terminalSide", "x", "y", "seq")); + propertyBag.put("terminalEquipment", terminalEquipment); + propertyBag.put("terminalSide", terminalSide); + propertyBag.put("x", Double.toString(x)); + propertyBag.put("y", Double.toString(y)); + propertyBag.put("seq", Integer.toString(seq)); + propertyBag.put("diagramName", diagramName); + return propertyBag; + } + + protected PropertyBag createTerminalPropertyBag(String terminalEquipment, String terminalSide, double x, double y, int seq) { + return createTerminalPropertyBag(terminalEquipment, terminalSide, x, y, seq, DEFAULT_DIAGRAM_NAME); + } + + protected PropertyBag createBusPropertyBag(String identifiedObject, String name, String voltageLevel, String vlName, double x, double y, int seq, String diagramName) { + PropertyBag propertyBag = new PropertyBag(Arrays.asList("identifiedObject", "name", "voltageLevel", "vlname", "x", "y", "seq")); + propertyBag.put("identifiedObject", identifiedObject); + propertyBag.put("name", name); + propertyBag.put("voltageLevel", voltageLevel); + propertyBag.put("vlname", vlName); + propertyBag.put("x", Double.toString(x)); + propertyBag.put("y", Double.toString(y)); + propertyBag.put("seq", Integer.toString(seq)); + propertyBag.put("diagramName", diagramName); + return propertyBag; + } + + protected PropertyBag createBusPropertyBag(String identifiedObject, String name, String voltageLevel, String vlName, double x, double y, int seq) { + return createBusPropertyBag(identifiedObject, name, voltageLevel, vlName, x, y, seq, DEFAULT_DIAGRAM_NAME); + } + + protected PropertyBag createBusbarPropertyBag(String identifiedObject, String name, double x, double y, int seq, String diagramName) { + PropertyBag propertyBag = new PropertyBag(Arrays.asList("identifiedObject", "name", "x", "y", "seq")); + propertyBag.put("busbarSection", identifiedObject); + propertyBag.put("name", name); + propertyBag.put("x", Double.toString(x)); + propertyBag.put("y", Double.toString(y)); + propertyBag.put("seq", Integer.toString(seq)); + propertyBag.put("diagramName", diagramName); + return propertyBag; + } + + protected PropertyBag createBusbarPropertyBag(String identifiedObject, String name, double x, double y, int seq) { + return createBusbarPropertyBag(identifiedObject, name, x, y, seq, DEFAULT_DIAGRAM_NAME); + } + + protected PropertyBag createSwitchPropertyBag(String identifiedObject, String name, double x, double y, int rotation, String diagramName) { + PropertyBag propertyBag = new PropertyBag(Arrays.asList("identifiedObject", "name", "x", "y", "rotation")); + propertyBag.put("identifiedObject", identifiedObject); + propertyBag.put("name", name); + propertyBag.put("x", Double.toString(x)); + propertyBag.put("y", Double.toString(y)); + propertyBag.put("rotation", Integer.toString(rotation)); + propertyBag.put("diagramName", diagramName); + return propertyBag; + } + + protected PropertyBag createSwitchPropertyBag(String identifiedObject, String name, double x, double y, int rotation) { + return createSwitchPropertyBag(identifiedObject, name, x, y, rotation, DEFAULT_DIAGRAM_NAME); + } + + private PropertyBag createTerminal(String terminal, int terminalSide, String equipment) { + PropertyBag propertyBag = new PropertyBag(Arrays.asList("terminal", "terminalSide", "equipment")); + propertyBag.put("terminal", terminal); + propertyBag.put("terminalSide", Integer.toString(terminalSide)); + propertyBag.put("equipment", equipment); + return propertyBag; + } + + private PropertyBag createBusbarNode(String busbar, String busbarNode) { + PropertyBag propertyBag = new PropertyBag(Arrays.asList("busbarNode", "busbarSection")); + propertyBag.put("busbarNode", busbarNode); + propertyBag.put("busbarSection", busbar); + return propertyBag; + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLImportPostProcessorTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLImportPostProcessorTest.java new file mode 100644 index 000000000..47bf7b79b --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLImportPostProcessorTest.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import com.powsybl.cgmes.conversion.Profiling; +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.iidm.network.Network; + +/** + * + * @author Massimo Ferraro + */ +public class CgmesDLImportPostProcessorTest extends CgmesDLModelTest { + + @Test + public void process() throws Exception { + Network network = Networks.createNetworkWithBusbar(); + new CgmesDLImportPostProcessor(queryCatalog).process(network, tripleStore, new Profiling()); + + BusbarSection busbar = network.getBusbarSection("Busbar"); + NodeDiagramData busDiagramData = busbar.getExtension(NodeDiagramData.class); + assertNotNull(busDiagramData); + assertNotNull(busDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + NodeDiagramData.NodeDiagramDataDetails nodeDiagramDataDetails = busDiagramData.getData(DEFAULT_DIAGRAM_NAME); + assertEquals(1, nodeDiagramDataDetails.getPoint1().getSeq(), 0); + assertEquals(20, nodeDiagramDataDetails.getPoint1().getX(), 0); + assertEquals(5, nodeDiagramDataDetails.getPoint1().getY(), 0); + assertEquals(2, nodeDiagramDataDetails.getPoint2().getSeq(), 0); + assertEquals(20, nodeDiagramDataDetails.getPoint2().getX(), 0); + assertEquals(40, nodeDiagramDataDetails.getPoint2().getY(), 0); + } +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLImporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLImporterTest.java new file mode 100644 index 000000000..4e9764297 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLImporterTest.java @@ -0,0 +1,297 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import com.powsybl.cgmes.iidm.extensions.dl.*; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.iidm.network.StaticVarCompensator; +import com.powsybl.iidm.network.Switch; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +import java.util.List; + +import static org.junit.Assert.*; + +/** + * + * @author Massimo Ferraro + */ +public class CgmesDLImporterTest extends AbstractCgmesDLTest { + + private CgmesDLModel cgmesDLModel; + + @Before + public void setUp() { + super.setUp(); + cgmesDLModel = Mockito.mock(CgmesDLModel.class); + Mockito.when(cgmesDLModel.getTerminalsDiagramData()).thenReturn(terminalsPropertyBags); + Mockito.when(cgmesDLModel.getBusesDiagramData()).thenReturn(busesPropertyBags); + Mockito.when(cgmesDLModel.getBusbarsDiagramData()).thenReturn(busbarsPropertyBags); + Mockito.when(cgmesDLModel.getLinesDiagramData()).thenReturn(linesPropertyBags); + Mockito.when(cgmesDLModel.getGeneratorsDiagramData()).thenReturn(generatorsPropertyBags); + Mockito.when(cgmesDLModel.getLoadsDiagramData()).thenReturn(loadsPropertyBags); + Mockito.when(cgmesDLModel.getShuntsDiagramData()).thenReturn(shuntsPropertyBags); + Mockito.when(cgmesDLModel.getSwitchesDiagramData()).thenReturn(switchesPropertyBags); + Mockito.when(cgmesDLModel.getTransformersDiagramData()).thenReturn(tranformersPropertyBags); + Mockito.when(cgmesDLModel.getHvdcLinesDiagramData()).thenReturn(hvdcLinesPropertyBags); + Mockito.when(cgmesDLModel.getSvcsDiagramData()).thenReturn(svcsPropertyBags); + } + + private void checkDiagramData(NodeDiagramData.NodeDiagramDataDetails diagramDetails) { + assertNotNull(diagramDetails); + assertEquals(1, diagramDetails.getPoint1().getSeq(), 0); + assertEquals(20, diagramDetails.getPoint1().getX(), 0); + assertEquals(5, diagramDetails.getPoint1().getY(), 0); + assertEquals(2, diagramDetails.getPoint2().getSeq(), 0); + assertEquals(20, diagramDetails.getPoint2().getX(), 0); + assertEquals(40, diagramDetails.getPoint2().getY(), 0); + } + + @Test + public void testBuses() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithBus(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + Bus bus = network.getVoltageLevel("VoltageLevel").getBusBreakerView().getBus("Bus"); + NodeDiagramData busDiagramData = bus.getExtension(NodeDiagramData.class); + + checkDiagramData(busDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + } + + @Test + public void testBusbars() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithBusbar(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + BusbarSection busbar = network.getBusbarSection("Busbar"); + NodeDiagramData busbarDiagramData = busbar.getExtension(NodeDiagramData.class); + + checkDiagramData(busbarDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + } + + private void checkDiagramData(LineDiagramData diagramData) { + assertNotNull(diagramData); + assertEquals(1, diagramData.getPoints(DEFAULT_DIAGRAM_NAME).get(0).getSeq(), 0); + assertEquals(20, diagramData.getPoints(DEFAULT_DIAGRAM_NAME).get(0).getX(), 0); + assertEquals(5, diagramData.getPoints(DEFAULT_DIAGRAM_NAME).get(0).getY(), 0); + assertEquals(2, diagramData.getPoints(DEFAULT_DIAGRAM_NAME).get(1).getSeq(), 0); + assertEquals(20, diagramData.getPoints(DEFAULT_DIAGRAM_NAME).get(1).getX(), 0); + assertEquals(40, diagramData.getPoints(DEFAULT_DIAGRAM_NAME).get(1).getY(), 0); + assertEquals(1, diagramData.getFirstPoint(DEFAULT_DIAGRAM_NAME).getSeq(), 0); + assertEquals(20, diagramData.getFirstPoint(DEFAULT_DIAGRAM_NAME).getX(), 0); + assertEquals(5, diagramData.getFirstPoint(DEFAULT_DIAGRAM_NAME).getY(), 0); + assertEquals(2, diagramData.getLastPoint(DEFAULT_DIAGRAM_NAME).getSeq(), 0); + assertEquals(20, diagramData.getLastPoint(DEFAULT_DIAGRAM_NAME).getX(), 0); + assertEquals(40, diagramData.getLastPoint(DEFAULT_DIAGRAM_NAME).getY(), 0); + } + + @Test + public void testLines() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithLine(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + Line line = network.getLine("Line"); + LineDiagramData lineDiagramData = line.getExtension(LineDiagramData.class); + + checkDiagramData(lineDiagramData); + } + + @Test + public void testDanglingLines() { + Mockito.when(cgmesDLModel.getLinesDiagramData()).thenReturn(danglingLinesPropertyBags); + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithDanglingLine(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + DanglingLine danglingLine = network.getDanglingLine("DanglingLine"); + LineDiagramData danglingLineDiagramData = danglingLine.getExtension(LineDiagramData.class); + + checkDiagramData(danglingLineDiagramData); + } + + @Test + public void testHvdcLines() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithHvdcLine(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + HvdcLine hdcvLine = network.getHvdcLine("HvdcLine"); + LineDiagramData hvdcLineDiagramData = hdcvLine.getExtension(LineDiagramData.class); + + checkDiagramData(hvdcLineDiagramData); + } + + private void checkDiagramData(InjectionDiagramData.InjectionDiagramDetails diagramDetails) { + assertNotNull(diagramDetails); + assertEquals(0, diagramDetails.getPoint().getSeq(), 0); + assertEquals(10, diagramDetails.getPoint().getX(), 0); + assertEquals(10, diagramDetails.getPoint().getY(), 0); + assertEquals(90, diagramDetails.getRotation(), 0); + + List points = diagramDetails.getTerminalPoints(); + assertEquals(1, points.get(0).getSeq(), 0); + assertEquals(2, points.get(0).getX(), 0); + assertEquals(10, points.get(0).getY(), 0); + assertEquals(2, points.get(1).getSeq(), 0); + assertEquals(6, points.get(1).getX(), 0); + assertEquals(10, points.get(1).getY(), 0); + } + + @Test + public void testGenerators() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithGenerator(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + Generator generator = network.getGenerator("Generator"); + InjectionDiagramData generatorDiagramData = generator.getExtension(InjectionDiagramData.class); + + checkDiagramData(generatorDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + } + + @Test + public void testLoads() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithLoad(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + Load load = network.getLoad("Load"); + InjectionDiagramData loadDiagramData = load.getExtension(InjectionDiagramData.class); + + checkDiagramData(loadDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + } + + @Test + public void testShunts() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithShuntCompensator(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + ShuntCompensator shunt = network.getShuntCompensator("Shunt"); + InjectionDiagramData shuntDiagramData = shunt.getExtension(InjectionDiagramData.class); + + checkDiagramData(shuntDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + } + + @Test + public void testSvcs() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithStaticVarCompensator(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + StaticVarCompensator svc = network.getStaticVarCompensator("Svc"); + InjectionDiagramData svcDiagramData = svc.getExtension(InjectionDiagramData.class); + + checkDiagramData(svcDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + } + + private void checkDiagramData(CouplingDeviceDiagramData.CouplingDeviceDiagramDetails diagramDataDetails) { + assertNotNull(diagramDataDetails); + assertEquals(0, diagramDataDetails.getPoint().getSeq(), 0); + assertEquals(10, diagramDataDetails.getPoint().getX(), 0); + assertEquals(10, diagramDataDetails.getPoint().getY(), 0); + assertEquals(90, diagramDataDetails.getRotation(), 0); + + List pointsT1 = diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL1); + List pointsT2 = diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL2); + assertEquals(1, pointsT1.get(0).getSeq(), 0); + assertEquals(2, pointsT1.get(0).getX(), 0); + assertEquals(10, pointsT1.get(0).getY(), 0); + assertEquals(2, pointsT1.get(1).getSeq(), 0); + assertEquals(6, pointsT1.get(1).getX(), 0); + assertEquals(10, pointsT1.get(1).getY(), 0); + assertEquals(1, pointsT2.get(0).getSeq(), 0); + assertEquals(14, pointsT2.get(0).getX(), 0); + assertEquals(10, pointsT2.get(0).getY(), 0); + assertEquals(2, pointsT2.get(1).getSeq(), 0); + assertEquals(18, pointsT2.get(1).getX(), 0); + assertEquals(10, pointsT2.get(1).getY(), 0); + } + + @Test + public void testSwitches() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithSwitch(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + Switch sw = network.getSwitch("Switch"); + CouplingDeviceDiagramData shuntDiagramData = sw.getExtension(CouplingDeviceDiagramData.class); + + checkDiagramData(shuntDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + } + + @Test + public void testTransformers() { + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithTwoWindingsTransformer(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + TwoWindingsTransformer transformer = network.getTwoWindingsTransformer("Transformer"); + CouplingDeviceDiagramData transformerDiagramData = transformer.getExtension(CouplingDeviceDiagramData.class); + + checkDiagramData(transformerDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + } + + private void checkDiagramData(ThreeWindingsTransformerDiagramData.ThreeWindingsTransformerDiagramDataDetails diagramDataDetails) { + assertNotNull(diagramDataDetails); + assertEquals(0, diagramDataDetails.getPoint().getSeq(), 0); + assertEquals(10, diagramDataDetails.getPoint().getX(), 0); + assertEquals(13, diagramDataDetails.getPoint().getY(), 0); + assertEquals(90, diagramDataDetails.getRotation(), 0); + assertEquals(1, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL1).get(0).getSeq(), 0); + assertEquals(2, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL1).get(0).getX(), 0); + assertEquals(10, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL1).get(0).getY(), 0); + assertEquals(2, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL1).get(1).getSeq(), 0); + assertEquals(6, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL1).get(1).getX(), 0); + assertEquals(10, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL1).get(1).getY(), 0); + assertEquals(1, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL2).get(0).getSeq(), 0); + assertEquals(14, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL2).get(0).getX(), 0); + assertEquals(10, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL2).get(0).getY(), 0); + assertEquals(2, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL2).get(1).getSeq(), 0); + assertEquals(18, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL2).get(1).getX(), 0); + assertEquals(10, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL2).get(1).getY(), 0); + assertEquals(1, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL3).get(0).getSeq(), 0); + assertEquals(10, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL3).get(0).getX(), 0); + assertEquals(16, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL3).get(0).getY(), 0); + assertEquals(2, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL3).get(1).getSeq(), 0); + assertEquals(10, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL3).get(1).getX(), 0); + assertEquals(20, diagramDataDetails.getTerminalPoints(DiagramTerminal.TERMINAL3).get(1).getY(), 0); + } + + @Test + public void testTransformers3w() { + Mockito.when(cgmesDLModel.getTransformersDiagramData()).thenReturn(tranformers3wPropertyBags); + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithThreeWindingsTransformer(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + ThreeWindingsTransformer transformer = network.getThreeWindingsTransformer("Transformer3w"); + ThreeWindingsTransformerDiagramData transformerDiagramData = transformer.getExtension(ThreeWindingsTransformerDiagramData.class); + + checkDiagramData(transformerDiagramData.getData(DEFAULT_DIAGRAM_NAME)); + } + + @Test + public void testRemoveExtensions() { + Mockito.when(cgmesDLModel.getTransformersDiagramData()).thenReturn(tranformers3wPropertyBags); + CgmesDLImporter cgmesDLImporter = new CgmesDLImporter(Networks.createNetworkWithLoad(), cgmesDLModel); + cgmesDLImporter.importDLData(); + Network network = cgmesDLImporter.getNetworkWithDLData(); + Load load = network.getLoad("Load"); + + assertNotNull(load.getExtension(InjectionDiagramData.class)); + CgmesDLUtils.removeIidmCgmesExtensions(network); + CgmesDLUtils.clearCgmesDl(network); + assertNull(load.getExtension(InjectionDiagramData.class)); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLModelTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLModelTest.java new file mode 100644 index 000000000..cb9ab087c --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/CgmesDLModelTest.java @@ -0,0 +1,150 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.powsybl.triplestore.api.PropertyBags; +import com.powsybl.triplestore.api.QueryCatalog; +import com.powsybl.triplestore.api.TripleStore; + +/** + * + * @author Massimo Ferraro + */ +public class CgmesDLModelTest extends AbstractCgmesDLTest { + + protected TripleStore tripleStore; + protected QueryCatalog queryCatalog; + protected CgmesDLModel cgmesDLModel; + + @Before + public void setUp() { + super.setUp(); + tripleStore = Mockito.mock(TripleStore.class); + queryCatalog = Mockito.mock(QueryCatalog.class); + setQuery(CgmesDLModel.TERMINAL_DIAGRAM_DATA_QUERY_KEY, "TerminalQuery", terminalsPropertyBags); + setQuery(CgmesDLModel.BUS_DIAGRAM_DATA_QUERY_KEY, "BusQuery", busesPropertyBags); + setQuery(CgmesDLModel.BUSBAR_DIAGRAM_DATA_QUERY_KEY, "BusbarQuery", busbarsPropertyBags); + setQuery(CgmesDLModel.LINE_DIAGRAM_DATA_QUERY_KEY, "LineQuery", linesPropertyBags); + setQuery(CgmesDLModel.GENERATOR_DIAGRAM_DATA_QUERY_KEY, "GeneratorQuery", generatorsPropertyBags); + setQuery(CgmesDLModel.LOAD_DIAGRAM_DATA_QUERY_KEY, "LoadQuery", loadsPropertyBags); + setQuery(CgmesDLModel.SHUNT_DIAGRAM_DATA_QUERY_KEY, "ShuntQuery", shuntsPropertyBags); + setQuery(CgmesDLModel.SWITCH_DIAGRAM_DATA_QUERY_KEY, "SwitchQuery", switchesPropertyBags); + setQuery(CgmesDLModel.TRANSFORMER_DIAGRAM_DATA_QUERY_KEY, "TransformerQuery", tranformersPropertyBags); + setQuery(CgmesDLModel.HVDC_LINE_DIAGRAM_DATA_QUERY_KEY, "HvdcLineQuery", hvdcLinesPropertyBags); + setQuery(CgmesDLModel.SVC_DIAGRAM_DATA_QUERY_KEY, "SvcLineQuery", svcsPropertyBags); + setQuery(CgmesDLModel.TERMINALS_QUERY_KEY, "TerminalsQuery", terminals); + setQuery(CgmesDLModel.BUSBAR_NODES_QUERY_KEY, "BusbarNodesQuery", busbarNodes); + cgmesDLModel = new CgmesDLModel(tripleStore, queryCatalog); + } + + protected void setQuery(String key, String query, PropertyBags queryResults) { + Mockito.when(queryCatalog.get(key)).thenReturn(query); + Mockito.when(tripleStore.query(query)).thenReturn(queryResults); + } + + protected void removeQueryCatalogKey(String key) { + Mockito.when(queryCatalog.get(key)).thenReturn(null); + } + + @Test + public void getTerminalsDiagramData() { + assertEquals(terminalsPropertyBags, cgmesDLModel.getTerminalsDiagramData()); + removeQueryCatalogKey(CgmesDLModel.TERMINAL_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getTerminalsDiagramData()); + } + + @Test + public void getBusesDiagramData() { + assertEquals(busesPropertyBags, cgmesDLModel.getBusesDiagramData()); + removeQueryCatalogKey(CgmesDLModel.BUS_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getBusesDiagramData()); + } + + @Test + public void getBusbarsDiagramData() { + assertEquals(busbarsPropertyBags, cgmesDLModel.getBusbarsDiagramData()); + removeQueryCatalogKey(CgmesDLModel.BUSBAR_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getBusbarsDiagramData()); + } + + @Test + public void getLinesDiagramData() { + assertEquals(busbarsPropertyBags, cgmesDLModel.getLinesDiagramData()); + removeQueryCatalogKey(CgmesDLModel.LINE_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getLinesDiagramData()); + } + + @Test + public void getGeneratorsDiagramData() { + assertEquals(generatorsPropertyBags, cgmesDLModel.getGeneratorsDiagramData()); + removeQueryCatalogKey(CgmesDLModel.GENERATOR_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getGeneratorsDiagramData()); + } + + @Test + public void getLoadsDiagramData() { + assertEquals(loadsPropertyBags, cgmesDLModel.getLoadsDiagramData()); + removeQueryCatalogKey(CgmesDLModel.LOAD_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getLoadsDiagramData()); + } + + @Test + public void getShuntsDiagramData() { + assertEquals(shuntsPropertyBags, cgmesDLModel.getShuntsDiagramData()); + removeQueryCatalogKey(CgmesDLModel.SHUNT_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getShuntsDiagramData()); + } + + @Test + public void getSwitchesDiagramData() { + assertEquals(switchesPropertyBags, cgmesDLModel.getSwitchesDiagramData()); + removeQueryCatalogKey(CgmesDLModel.SWITCH_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getSwitchesDiagramData()); + } + + @Test + public void getTransformersDiagramData() { + assertEquals(tranformersPropertyBags, cgmesDLModel.getTransformersDiagramData()); + removeQueryCatalogKey(CgmesDLModel.TRANSFORMER_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getTransformersDiagramData()); + } + + @Test + public void getHvdcLineDiagramData() { + assertEquals(hvdcLinesPropertyBags, cgmesDLModel.getHvdcLinesDiagramData()); + removeQueryCatalogKey(CgmesDLModel.HVDC_LINE_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getHvdcLinesDiagramData()); + } + + @Test + public void getSvcsDiagramData() { + assertEquals(svcsPropertyBags, cgmesDLModel.getSvcsDiagramData()); + removeQueryCatalogKey(CgmesDLModel.SVC_DIAGRAM_DATA_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getSvcsDiagramData()); + } + + @Test + public void getTerminals() { + assertEquals(terminals, cgmesDLModel.getTerminals()); + removeQueryCatalogKey(CgmesDLModel.TERMINALS_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getTerminals()); + } + + @Test + public void getBusbarNodes() { + assertEquals(busbarNodes, cgmesDLModel.getBusbarNodes()); + removeQueryCatalogKey(CgmesDLModel.BUSBAR_NODES_QUERY_KEY); + assertEquals(new PropertyBags(), cgmesDLModel.getBusbarNodes()); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractCouplingDeviceDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractCouplingDeviceDiagramDataExporterTest.java new file mode 100644 index 000000000..132d58459 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractCouplingDeviceDiagramDataExporterTest.java @@ -0,0 +1,69 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Arrays; + +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.dl.conversion.AbstractCgmesDLExporterTest; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractCouplingDeviceDiagramDataExporterTest extends AbstractCgmesDLExporterTest { + protected final DiagramPoint point = new DiagramPoint(20, 10, 0); + protected final double rotation = 90; + protected final DiagramPoint terminal1Point1 = new DiagramPoint(5, 10, 1); + protected final DiagramPoint terminal1Point2 = new DiagramPoint(15, 10, 2); + protected final String terminal1Id = "terminal1Id"; + protected final DiagramPoint terminal2Point1 = new DiagramPoint(25, 10, 1); + protected final DiagramPoint terminal2Point2 = new DiagramPoint(35, 10, 2); + protected final String terminal2Id = "terminal2Id"; + + @Before + public void setUp() { + super.setUp(); + + Mockito.when(cgmesDLModel.getBusbarNodes()).thenReturn(new PropertyBags()); + } + + protected PropertyBags getTerminals(String injectionId) { + return new PropertyBags(Arrays.asList(getTerminal(injectionId, terminal1Id, 1), + getTerminal(injectionId, terminal2Id, 2))); + } + + protected void checkStatements(String couplingDeviceId, String injectionName, String styleName) { + Mockito.verify(tripleStore, Mockito.times(11)).add(contextCaptor.capture(), nsCaptor.capture(), + typeCaptor.capture(), propertiesCaptor.capture()); + checkDiagram(contextCaptor.getAllValues().get(1), nsCaptor.getAllValues().get(1), typeCaptor.getAllValues().get(1), + propertiesCaptor.getAllValues().get(1)); + checkDiagramObjectStyle(contextCaptor.getAllValues().get(2), nsCaptor.getAllValues().get(2), typeCaptor.getAllValues().get(2), + propertiesCaptor.getAllValues().get(2), styleName); + checkDiagramObject(contextCaptor.getAllValues().get(3), nsCaptor.getAllValues().get(3), typeCaptor.getAllValues().get(3), + propertiesCaptor.getAllValues().get(3), injectionName, couplingDeviceId, rotation); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(4), nsCaptor.getAllValues().get(4), typeCaptor.getAllValues().get(4), + propertiesCaptor.getAllValues().get(4), point); + checkDiagramObject(contextCaptor.getAllValues().get(5), nsCaptor.getAllValues().get(5), typeCaptor.getAllValues().get(5), + propertiesCaptor.getAllValues().get(5), injectionName + "_0", terminal1Id, 0); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(6), nsCaptor.getAllValues().get(6), typeCaptor.getAllValues().get(6), + propertiesCaptor.getAllValues().get(6), terminal1Point1); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(7), nsCaptor.getAllValues().get(7), typeCaptor.getAllValues().get(7), + propertiesCaptor.getAllValues().get(7), terminal1Point2); + checkDiagramObject(contextCaptor.getAllValues().get(8), nsCaptor.getAllValues().get(8), typeCaptor.getAllValues().get(8), + propertiesCaptor.getAllValues().get(8), injectionName + "_1", terminal2Id, 0); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(9), nsCaptor.getAllValues().get(9), typeCaptor.getAllValues().get(9), + propertiesCaptor.getAllValues().get(9), terminal2Point1); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(10), nsCaptor.getAllValues().get(10), typeCaptor.getAllValues().get(10), + propertiesCaptor.getAllValues().get(10), terminal2Point2); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractInjectionDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractInjectionDiagramDataExporterTest.java new file mode 100644 index 000000000..6fc8a1b9c --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractInjectionDiagramDataExporterTest.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Collections; + +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.dl.conversion.AbstractCgmesDLExporterTest; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractInjectionDiagramDataExporterTest extends AbstractCgmesDLExporterTest { + protected final DiagramPoint point = new DiagramPoint(20, 10, 0); + protected final double rotation = 90; + protected final DiagramPoint terminalPoint1 = new DiagramPoint(5, 10, 1); + protected final DiagramPoint terminalPoint2 = new DiagramPoint(15, 10, 2); + protected final String terminalId = "terminalId"; + + @Before + public void setUp() { + super.setUp(); + + Mockito.when(cgmesDLModel.getBusbarNodes()).thenReturn(new PropertyBags()); + } + + protected PropertyBags getTerminals(String injectionId) { + return new PropertyBags(Collections.singleton(getTerminal(injectionId, terminalId, 1))); + } + + protected void checkStatements(String injectionId, String injectionName, String styleName) { + Mockito.verify(tripleStore, Mockito.times(8)).add(contextCaptor.capture(), nsCaptor.capture(), + typeCaptor.capture(), propertiesCaptor.capture()); + checkDiagram(contextCaptor.getAllValues().get(1), nsCaptor.getAllValues().get(1), typeCaptor.getAllValues().get(1), + propertiesCaptor.getAllValues().get(1)); + checkDiagramObjectStyle(contextCaptor.getAllValues().get(2), nsCaptor.getAllValues().get(2), typeCaptor.getAllValues().get(2), + propertiesCaptor.getAllValues().get(2), styleName); + checkDiagramObject(contextCaptor.getAllValues().get(3), nsCaptor.getAllValues().get(3), typeCaptor.getAllValues().get(3), + propertiesCaptor.getAllValues().get(3), injectionName, injectionId, rotation); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(4), nsCaptor.getAllValues().get(4), typeCaptor.getAllValues().get(4), + propertiesCaptor.getAllValues().get(4), point); + checkDiagramObject(contextCaptor.getAllValues().get(5), nsCaptor.getAllValues().get(5), typeCaptor.getAllValues().get(5), + propertiesCaptor.getAllValues().get(5), injectionName + "_0", terminalId, 0); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(6), nsCaptor.getAllValues().get(6), typeCaptor.getAllValues().get(6), + propertiesCaptor.getAllValues().get(6), terminalPoint1); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(7), nsCaptor.getAllValues().get(7), typeCaptor.getAllValues().get(7), + propertiesCaptor.getAllValues().get(7), terminalPoint2); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractNodeLineDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractNodeLineDiagramDataExporterTest.java new file mode 100644 index 000000000..ce325b618 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/AbstractNodeLineDiagramDataExporterTest.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.dl.conversion.AbstractCgmesDLExporterTest; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractNodeLineDiagramDataExporterTest extends AbstractCgmesDLExporterTest { + + protected final DiagramPoint point1 = new DiagramPoint(0, 0, 1); + protected final DiagramPoint point2 = new DiagramPoint(10, 0, 2); + + @Before + public void setUp() { + super.setUp(); + + Mockito.when(cgmesDLModel.getTerminals()).thenReturn(new PropertyBags()); + } + + protected void checkStatements(String nodeId, String nodeName, String styleName) { + Mockito.verify(tripleStore, Mockito.times(6)).add(contextCaptor.capture(), nsCaptor.capture(), + typeCaptor.capture(), propertiesCaptor.capture()); + checkDiagram(contextCaptor.getAllValues().get(1), nsCaptor.getAllValues().get(1), typeCaptor.getAllValues().get(1), + propertiesCaptor.getAllValues().get(1)); + checkDiagramObjectStyle(contextCaptor.getAllValues().get(2), nsCaptor.getAllValues().get(2), typeCaptor.getAllValues().get(2), + propertiesCaptor.getAllValues().get(2), styleName); + checkDiagramObject(contextCaptor.getAllValues().get(3), nsCaptor.getAllValues().get(3), typeCaptor.getAllValues().get(3), + propertiesCaptor.getAllValues().get(3), nodeName, nodeId, 0); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(4), nsCaptor.getAllValues().get(4), typeCaptor.getAllValues().get(4), + propertiesCaptor.getAllValues().get(4), point1); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(5), nsCaptor.getAllValues().get(5), typeCaptor.getAllValues().get(5), + propertiesCaptor.getAllValues().get(5), point2); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/BusDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/BusDiagramDataExporterTest.java new file mode 100644 index 000000000..496f576ae --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/BusDiagramDataExporterTest.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.iidm.network.Bus; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class BusDiagramDataExporterTest extends AbstractNodeLineDiagramDataExporterTest { + + private Bus bus; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithBus(); + bus = network.getVoltageLevel("VoltageLevel").getBusBreakerView().getBus("Bus"); + NodeDiagramData busDiagramData = new NodeDiagramData<>(bus); + NodeDiagramData.NodeDiagramDataDetails details = busDiagramData.new NodeDiagramDataDetails(); + details.setPoint1(point1); + details.setPoint2(point2); + busDiagramData.addData(basename, details); + bus.addExtension(NodeDiagramData.class, busDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getBusbarNodes()).thenReturn(new PropertyBags()); + } + + @Override + protected void checkStatements() { + checkStatements(bus.getId(), bus.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/BusbarDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/BusbarDiagramDataExporterTest.java new file mode 100644 index 000000000..3f7430562 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/BusbarDiagramDataExporterTest.java @@ -0,0 +1,57 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Arrays; +import java.util.Collections; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.triplestore.api.PropertyBag; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class BusbarDiagramDataExporterTest extends AbstractNodeLineDiagramDataExporterTest { + + private BusbarSection busbar; + private final String busbarNodeId = "busbarNodeId"; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithBusbar(); + busbar = network.getVoltageLevel("VoltageLevel").getNodeBreakerView().getBusbarSection("Busbar"); + NodeDiagramData busbarDiagramData = new NodeDiagramData<>(busbar); + NodeDiagramData.NodeDiagramDataDetails details = busbarDiagramData.new NodeDiagramDataDetails(); + details.setPoint1(point1); + details.setPoint2(point2); + busbarDiagramData.addData(basename, details); + busbar.addExtension(NodeDiagramData.class, busbarDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + PropertyBag busbarNode = new PropertyBag(Arrays.asList("busbarSection", "busbarNode")); + busbarNode.put("busbarSection", dataNs + busbar.getId()); + busbarNode.put("busbarNode", dataNs + busbarNodeId); + PropertyBags busbarNodes = new PropertyBags(Collections.singleton(busbarNode)); + Mockito.when(cgmesDLModel.getBusbarNodes()).thenReturn(busbarNodes); + } + + @Override + protected void checkStatements() { + checkStatements(busbarNodeId, busbar.getName(), "node-breaker"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/DanglingLineDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/DanglingLineDiagramDataExporterTest.java new file mode 100644 index 000000000..025316ba4 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/DanglingLineDiagramDataExporterTest.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class DanglingLineDiagramDataExporterTest extends AbstractNodeLineDiagramDataExporterTest { + + private DanglingLine danglingLine; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithDanglingLine(); + danglingLine = network.getDanglingLine("DanglingLine"); + LineDiagramData danglingLineDiagramData = new LineDiagramData<>(danglingLine); + danglingLineDiagramData.addPoint(basename, point1); + danglingLineDiagramData.addPoint(basename, point2); + danglingLine.addExtension(LineDiagramData.class, danglingLineDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getBusbarNodes()).thenReturn(new PropertyBags()); + } + + @Override + protected void checkStatements() { + checkStatements(danglingLine.getId(), danglingLine.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/GeneratorDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/GeneratorDiagramDataExporterTest.java new file mode 100644 index 000000000..49e76c91e --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/GeneratorDiagramDataExporterTest.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.Generator; + +/** + * + * @author Massimo Ferraro + */ +public class GeneratorDiagramDataExporterTest extends AbstractInjectionDiagramDataExporterTest { + + private Generator generator; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithGenerator(); + generator = network.getGenerator("Generator"); + InjectionDiagramData generatorDiagramData = new InjectionDiagramData<>(generator); + InjectionDiagramData.InjectionDiagramDetails details = generatorDiagramData.new InjectionDiagramDetails(point, rotation); + details.addTerminalPoint(terminalPoint1); + details.addTerminalPoint(terminalPoint2); + generatorDiagramData.addData(basename, details); + generator.addExtension(InjectionDiagramData.class, generatorDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getTerminals()).thenReturn(getTerminals(generator.getId())); + } + + @Override + protected void checkStatements() { + checkStatements(generator.getId(), generator.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/HvdcLineDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/HvdcLineDiagramDataExporterTest.java new file mode 100644 index 000000000..1fac2f635 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/HvdcLineDiagramDataExporterTest.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class HvdcLineDiagramDataExporterTest extends AbstractNodeLineDiagramDataExporterTest { + + private HvdcLine hvdcLine; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithHvdcLine(); + hvdcLine = network.getHvdcLine("HvdcLine"); + LineDiagramData hvdcLineDiagramData = new LineDiagramData<>(hvdcLine); + hvdcLineDiagramData.addPoint(basename, point1); + hvdcLineDiagramData.addPoint(basename, point2); + hvdcLine.addExtension(LineDiagramData.class, hvdcLineDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getBusbarNodes()).thenReturn(new PropertyBags()); + } + + @Override + protected void checkStatements() { + checkStatements(hvdcLine.getId(), hvdcLine.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/LineDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/LineDiagramDataExporterTest.java new file mode 100644 index 000000000..0299bfae7 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/LineDiagramDataExporterTest.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.iidm.network.Line; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class LineDiagramDataExporterTest extends AbstractNodeLineDiagramDataExporterTest { + + private Line line; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithLine(); + line = network.getLine("Line"); + LineDiagramData lineDiagramData = new LineDiagramData<>(line); + lineDiagramData.addPoint(basename, point1); + lineDiagramData.addPoint(basename, point2); + line.addExtension(LineDiagramData.class, lineDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getBusbarNodes()).thenReturn(new PropertyBags()); + } + + @Override + protected void checkStatements() { + checkStatements(line.getId(), line.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/LoadDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/LoadDiagramDataExporterTest.java new file mode 100644 index 000000000..fc5105e6b --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/LoadDiagramDataExporterTest.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.Load; + +/** + * + * @author Massimo Ferraro + */ +public class LoadDiagramDataExporterTest extends AbstractInjectionDiagramDataExporterTest { + private Load load; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithLoad(); + load = network.getLoad("Load"); + InjectionDiagramData loadDiagramData = new InjectionDiagramData<>(load); + InjectionDiagramData.InjectionDiagramDetails details = loadDiagramData.new InjectionDiagramDetails(point, rotation); + details.addTerminalPoint(terminalPoint1); + details.addTerminalPoint(terminalPoint2); + loadDiagramData.addData(basename, details); + load.addExtension(InjectionDiagramData.class, loadDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getTerminals()).thenReturn(getTerminals(load.getId())); + } + + @Override + protected void checkStatements() { + checkStatements(load.getId(), load.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/ShuntDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/ShuntDiagramDataExporterTest.java new file mode 100644 index 000000000..837234b44 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/ShuntDiagramDataExporterTest.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.ShuntCompensator; + +/** + * + * @author Massimo Ferraro + */ +public class ShuntDiagramDataExporterTest extends AbstractInjectionDiagramDataExporterTest { + + private ShuntCompensator shunt; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithShuntCompensator(); + shunt = network.getShuntCompensator("Shunt"); + InjectionDiagramData shuntDiagramData = new InjectionDiagramData<>(shunt); + InjectionDiagramData.InjectionDiagramDetails details = shuntDiagramData.new InjectionDiagramDetails(point, rotation); + details.addTerminalPoint(terminalPoint1); + details.addTerminalPoint(terminalPoint2); + shuntDiagramData.addData(basename, details); + shunt.addExtension(InjectionDiagramData.class, shuntDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getTerminals()).thenReturn(getTerminals(shunt.getId())); + } + + @Override + protected void checkStatements() { + checkStatements(shunt.getId(), shunt.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/SvcDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/SvcDiagramDataExporterTest.java new file mode 100644 index 000000000..f84f99621 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/SvcDiagramDataExporterTest.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.iidm.network.StaticVarCompensator; + +/** + * + * @author Massimo Ferraro + */ +public class SvcDiagramDataExporterTest extends AbstractInjectionDiagramDataExporterTest { + + private StaticVarCompensator svc; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithStaticVarCompensator(); + svc = network.getStaticVarCompensator("Svc"); + InjectionDiagramData svcDiagramData = new InjectionDiagramData<>(svc); + InjectionDiagramData.InjectionDiagramDetails details = svcDiagramData.new InjectionDiagramDetails(point, rotation); + details.addTerminalPoint(terminalPoint1); + details.addTerminalPoint(terminalPoint2); + svcDiagramData.addData(basename, details); + svc.addExtension(InjectionDiagramData.class, svcDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getTerminals()).thenReturn(getTerminals(svc.getId())); + } + + @Override + protected void checkStatements() { + checkStatements(svc.getId(), svc.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/SwitchDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/SwitchDiagramDataExporterTest.java new file mode 100644 index 000000000..f4be12806 --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/SwitchDiagramDataExporterTest.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.CouplingDeviceDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramTerminal; +import com.powsybl.iidm.network.Switch; + +/** + * + * @author Massimo Ferraro + */ +public class SwitchDiagramDataExporterTest extends AbstractCouplingDeviceDiagramDataExporterTest { + + private Switch sw; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithSwitch(); + sw = network.getSwitch("Switch"); + CouplingDeviceDiagramData switchDiagramData = new CouplingDeviceDiagramData<>(sw); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails details = switchDiagramData.new CouplingDeviceDiagramDetails(point, rotation); + details.addTerminalPoint(DiagramTerminal.TERMINAL1, terminal1Point1); + details.addTerminalPoint(DiagramTerminal.TERMINAL1, terminal1Point2); + details.addTerminalPoint(DiagramTerminal.TERMINAL2, terminal2Point1); + details.addTerminalPoint(DiagramTerminal.TERMINAL2, terminal2Point2); + switchDiagramData.addData(basename, details); + sw.addExtension(CouplingDeviceDiagramData.class, switchDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getTerminals()).thenReturn(getTerminals(sw.getId())); + } + + @Override + protected void checkStatements() { + checkStatements(sw.getId(), sw.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/Transformer3WDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/Transformer3WDiagramDataExporterTest.java new file mode 100644 index 000000000..f139a65fe --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/Transformer3WDiagramDataExporterTest.java @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import java.util.Arrays; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.dl.conversion.AbstractCgmesDLExporterTest; +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramTerminal; +import com.powsybl.cgmes.iidm.extensions.dl.ThreeWindingsTransformerDiagramData; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.triplestore.api.PropertyBags; + +/** + * + * @author Massimo Ferraro + */ +public class Transformer3WDiagramDataExporterTest extends AbstractCgmesDLExporterTest { + + private final DiagramPoint point = new DiagramPoint(20, 13, 0); + private final double rotation = 90; + private final DiagramPoint terminal1Point1 = new DiagramPoint(5, 10, 1); + private final DiagramPoint terminal1Point2 = new DiagramPoint(15, 10, 2); + private final String terminal1Id = "terminal1Id"; + private final DiagramPoint terminal2Point1 = new DiagramPoint(25, 10, 1); + private final DiagramPoint terminal2Point2 = new DiagramPoint(35, 10, 2); + private final String terminal2Id = "terminal2Id"; + private final DiagramPoint terminal3Point1 = new DiagramPoint(20, 16, 1); + private final DiagramPoint terminal3Point2 = new DiagramPoint(20, 30, 2); + private final String terminal3Id = "terminal3Id"; + private ThreeWindingsTransformer twt; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithThreeWindingsTransformer(); + twt = network.getThreeWindingsTransformer("Transformer3w"); + ThreeWindingsTransformerDiagramData twtDiagramData = new ThreeWindingsTransformerDiagramData(twt); + ThreeWindingsTransformerDiagramData.ThreeWindingsTransformerDiagramDataDetails details = twtDiagramData.new ThreeWindingsTransformerDiagramDataDetails(point, rotation); + details.addTerminalPoint(DiagramTerminal.TERMINAL1, terminal1Point1); + details.addTerminalPoint(DiagramTerminal.TERMINAL1, terminal1Point2); + details.addTerminalPoint(DiagramTerminal.TERMINAL2, terminal2Point1); + details.addTerminalPoint(DiagramTerminal.TERMINAL2, terminal2Point2); + details.addTerminalPoint(DiagramTerminal.TERMINAL3, terminal3Point1); + details.addTerminalPoint(DiagramTerminal.TERMINAL3, terminal3Point2); + twtDiagramData.addData(basename, details); + twt.addExtension(ThreeWindingsTransformerDiagramData.class, twtDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getTerminals()).thenReturn(getTerminals(twt.getId())); + Mockito.when(cgmesDLModel.getBusbarNodes()).thenReturn(new PropertyBags()); + } + + protected PropertyBags getTerminals(String injectionId) { + return new PropertyBags(Arrays.asList(getTerminal(injectionId, terminal1Id, 1), + getTerminal(injectionId, terminal2Id, 2), + getTerminal(injectionId, terminal3Id, 3))); + } + + protected void checkStatements(String couplingDeviceId, String injectionName, String styleName) { + Mockito.verify(tripleStore, Mockito.times(14)).add(contextCaptor.capture(), nsCaptor.capture(), + typeCaptor.capture(), propertiesCaptor.capture()); + checkDiagram(contextCaptor.getAllValues().get(1), nsCaptor.getAllValues().get(1), typeCaptor.getAllValues().get(1), + propertiesCaptor.getAllValues().get(1)); + checkDiagramObjectStyle(contextCaptor.getAllValues().get(2), nsCaptor.getAllValues().get(2), typeCaptor.getAllValues().get(2), + propertiesCaptor.getAllValues().get(2), styleName); + checkDiagramObject(contextCaptor.getAllValues().get(3), nsCaptor.getAllValues().get(3), typeCaptor.getAllValues().get(3), + propertiesCaptor.getAllValues().get(3), injectionName, couplingDeviceId, rotation); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(4), nsCaptor.getAllValues().get(4), typeCaptor.getAllValues().get(4), + propertiesCaptor.getAllValues().get(4), point); + checkDiagramObject(contextCaptor.getAllValues().get(5), nsCaptor.getAllValues().get(5), typeCaptor.getAllValues().get(5), + propertiesCaptor.getAllValues().get(5), injectionName + "_0", terminal1Id, 0); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(6), nsCaptor.getAllValues().get(6), typeCaptor.getAllValues().get(6), + propertiesCaptor.getAllValues().get(6), terminal1Point1); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(7), nsCaptor.getAllValues().get(7), typeCaptor.getAllValues().get(7), + propertiesCaptor.getAllValues().get(7), terminal1Point2); + checkDiagramObject(contextCaptor.getAllValues().get(8), nsCaptor.getAllValues().get(8), typeCaptor.getAllValues().get(8), + propertiesCaptor.getAllValues().get(8), injectionName + "_1", terminal2Id, 0); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(9), nsCaptor.getAllValues().get(9), typeCaptor.getAllValues().get(9), + propertiesCaptor.getAllValues().get(9), terminal2Point1); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(10), nsCaptor.getAllValues().get(10), typeCaptor.getAllValues().get(10), + propertiesCaptor.getAllValues().get(10), terminal2Point2); + checkDiagramObject(contextCaptor.getAllValues().get(11), nsCaptor.getAllValues().get(11), typeCaptor.getAllValues().get(11), + propertiesCaptor.getAllValues().get(11), injectionName + "_2", terminal3Id, 0); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(12), nsCaptor.getAllValues().get(12), typeCaptor.getAllValues().get(12), + propertiesCaptor.getAllValues().get(12), terminal3Point1); + checkDiagramObjectPoint(contextCaptor.getAllValues().get(13), nsCaptor.getAllValues().get(13), typeCaptor.getAllValues().get(13), + propertiesCaptor.getAllValues().get(13), terminal3Point2); + } + + @Override + protected void checkStatements() { + checkStatements(twt.getId(), twt.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/TransformerDiagramDataExporterTest.java b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/TransformerDiagramDataExporterTest.java new file mode 100644 index 000000000..bc04d5d9c --- /dev/null +++ b/cgmes-dl/cgmes-dl-conversion/src/test/java/com/powsybl/cgmes/dl/conversion/exporters/TransformerDiagramDataExporterTest.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.dl.conversion.exporters; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import org.junit.Before; +import org.mockito.Mockito; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.CouplingDeviceDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramTerminal; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +/** + * + * @author Massimo Ferraro + */ +public class TransformerDiagramDataExporterTest extends AbstractCouplingDeviceDiagramDataExporterTest { + private TwoWindingsTransformer twt; + + @Before + public void setUp() { + super.setUp(); + + network = Networks.createNetworkWithTwoWindingsTransformer(); + twt = network.getTwoWindingsTransformer("Transformer"); + CouplingDeviceDiagramData twtDiagramData = new CouplingDeviceDiagramData<>(twt); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails details = twtDiagramData.new CouplingDeviceDiagramDetails(point, rotation); + details.addTerminalPoint(DiagramTerminal.TERMINAL1, terminal1Point1); + details.addTerminalPoint(DiagramTerminal.TERMINAL1, terminal1Point2); + details.addTerminalPoint(DiagramTerminal.TERMINAL2, terminal2Point1); + details.addTerminalPoint(DiagramTerminal.TERMINAL2, terminal2Point2); + twtDiagramData.addData(basename, details); + twt.addExtension(CouplingDeviceDiagramData.class, twtDiagramData); + NetworkDiagramData.addDiagramName(network, basename); + + Mockito.when(cgmesDLModel.getTerminals()).thenReturn(getTerminals(twt.getId())); + } + + @Override + protected void checkStatements() { + checkStatements(twt.getId(), twt.getName(), "bus-branch"); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/pom.xml b/cgmes-dl/cgmes-iidm-extensions/pom.xml new file mode 100644 index 000000000..c8b0be928 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/pom.xml @@ -0,0 +1,59 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-cgmes-dl + 1.0.0-SNAPSHOT + + + powsybl-cgmes-iidm-extensions + CGMES IIDM extensions + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + + + + + com.powsybl + powsybl-iidm-api + + + + com.powsybl + powsybl-iidm-impl + test + + + junit + junit + test + + + org.slf4j + slf4j-simple + test + + + \ No newline at end of file diff --git a/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/CouplingDeviceDiagramData.java b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/CouplingDeviceDiagramData.java new file mode 100644 index 000000000..501c8f4aa --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/CouplingDeviceDiagramData.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.commons.extensions.AbstractExtension; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.Switch; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * + * @author Massimo Ferraro + */ +public class CouplingDeviceDiagramData> extends AbstractExtension { + + static final String NAME = "coupling-device-diagram-data"; + + public class CouplingDeviceDiagramDetails { + private final DiagramPoint point; + private final double rotation; + private List terminal1Points = new ArrayList<>(); + private List terminal2Points = new ArrayList<>(); + + public CouplingDeviceDiagramDetails(DiagramPoint point, double rotation) { + this.point = Objects.requireNonNull(point); + this.rotation = Objects.requireNonNull(rotation); + } + + public DiagramPoint getPoint() { + return point; + } + + public double getRotation() { + return rotation; + } + + public List getTerminalPoints(DiagramTerminal terminal) { + Objects.requireNonNull(terminal); + switch (terminal) { + case TERMINAL1: + return terminal1Points.stream().sorted().collect(Collectors.toList()); + case TERMINAL2: + return terminal2Points.stream().sorted().collect(Collectors.toList()); + default: + throw new AssertionError("Unexpected terminal: " + terminal); + } + } + + public void addTerminalPoint(DiagramTerminal terminal, DiagramPoint point) { + Objects.requireNonNull(terminal); + Objects.requireNonNull(point); + switch (terminal) { + case TERMINAL1: + terminal1Points.add(point); + break; + case TERMINAL2: + terminal2Points.add(point); + break; + default: + throw new AssertionError("Unexpected terminal: " + terminal); + } + } + } + + private Map diagramsDetails = new HashMap<>(); + + private CouplingDeviceDiagramData(T extendable) { + super(extendable); + } + + public CouplingDeviceDiagramData(Switch sw) { + this((T) sw); + } + + public CouplingDeviceDiagramData(TwoWindingsTransformer transformer) { + this((T) transformer); + } + + @Override + public String getName() { + return NAME; + } + + public void addData(String diagramName, CouplingDeviceDiagramDetails data) { + Objects.requireNonNull(diagramName); + Objects.requireNonNull(data); + diagramsDetails.put(diagramName, data); + } + + public CouplingDeviceDiagramDetails getData(String diagramName) { + Objects.requireNonNull(diagramName); + return diagramsDetails.get(diagramName); + } + + public List getDiagramsNames() { + return new ArrayList<>(diagramsDetails.keySet()); + } +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/DiagramPoint.java b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/DiagramPoint.java new file mode 100644 index 000000000..cd449c2b7 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/DiagramPoint.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import java.util.Objects; + +/** + * + * @author Massimo Ferraro + */ +public class DiagramPoint implements Comparable { + + final double x; + final double y; + final int seq; + + public DiagramPoint(double x, double y, int seq) { + this.x = x; + this.y = y; + this.seq = seq; + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public int getSeq() { + return seq; + } + + @Override + public int hashCode() { + return Objects.hash(x, y, seq); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof DiagramPoint) { + return ((DiagramPoint) obj).compareTo(this) == 0; + } + return false; + } + + @Override + public int compareTo(DiagramPoint point) { + return this.seq - point.seq; + } + + @Override + public String toString() { + return "[" + String.join(",", Double.toString(x), Double.toString(y), Integer.toString(seq)) + "]"; + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/DiagramTerminal.java b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/DiagramTerminal.java new file mode 100644 index 000000000..046534b92 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/DiagramTerminal.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +/** + * + * @author Massimo Ferraro + */ +public enum DiagramTerminal { + TERMINAL1, + TERMINAL2, + TERMINAL3 +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/InjectionDiagramData.java b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/InjectionDiagramData.java new file mode 100644 index 000000000..ff0e26fba --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/InjectionDiagramData.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.commons.extensions.AbstractExtension; +import com.powsybl.iidm.network.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * + * @author Massimo Ferraro + */ +public class InjectionDiagramData> extends AbstractExtension { + + static final String NAME = "injection-diagram-data"; + + public class InjectionDiagramDetails { + private final DiagramPoint point; + private final double rotation; + private List terminalPoints = new ArrayList<>(); + + public InjectionDiagramDetails(DiagramPoint point, double rotation) { + this.point = Objects.requireNonNull(point); + this.rotation = Objects.requireNonNull(rotation); + } + + public void addTerminalPoint(DiagramPoint point) { + Objects.requireNonNull(point); + terminalPoints.add(point); + } + + public DiagramPoint getPoint() { + return point; + } + + public double getRotation() { + return rotation; + } + + public List getTerminalPoints() { + return terminalPoints.stream().sorted().collect(Collectors.toList()); + } + } + + private Map diagramsDetails = new HashMap<>(); + + private InjectionDiagramData(T injection) { + super(injection); + } + + public InjectionDiagramData(Generator generator) { + this((T) generator); + } + + public InjectionDiagramData(Load load) { + this((T) load); + } + + public InjectionDiagramData(ShuntCompensator shunt) { + this((T) shunt); + } + + public InjectionDiagramData(StaticVarCompensator svc) { + this((T) svc); + } + + @Override + public String getName() { + return NAME; + } + + public void addData(String diagramName, InjectionDiagramDetails diagramData) { + Objects.requireNonNull(diagramName); + Objects.requireNonNull(diagramData); + diagramsDetails.put(diagramName, diagramData); + } + + public InjectionDiagramDetails getData(String diagramName) { + Objects.requireNonNull(diagramName); + return diagramsDetails.get(diagramName); + } + + public List getDiagramsNames() { + return new ArrayList<>(diagramsDetails.keySet()); + } +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/LineDiagramData.java b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/LineDiagramData.java new file mode 100644 index 000000000..dd1973bad --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/LineDiagramData.java @@ -0,0 +1,117 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.commons.extensions.AbstractExtension; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.Identifiable; +import com.powsybl.iidm.network.Line; +import org.apache.commons.math3.geometry.euclidean.twod.Vector2D; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * + * @author Massimo Ferraro + */ +public class LineDiagramData> extends AbstractExtension { + + static final String NAME = "line-diagram-data"; + + private Map> diagramsDetails = new HashMap<>(); + + private LineDiagramData(T line) { + super(line); + } + + public LineDiagramData(Line line) { + this((T) line); + } + + public LineDiagramData(DanglingLine danglingLine) { + this((T) danglingLine); + } + + public LineDiagramData(HvdcLine hvdcLine) { + this((T) hvdcLine); + } + + @Override + public String getName() { + return NAME; + } + + public void addPoint(String diagramName, DiagramPoint point) { + Objects.requireNonNull(diagramName); + Objects.requireNonNull(point); + List points = diagramsDetails.getOrDefault(diagramName, new ArrayList<>()); + points.add(point); + diagramsDetails.put(diagramName, points); + } + + public List getPoints(String diagramName) { + return diagramsDetails.getOrDefault(diagramName, Collections.emptyList()).stream().sorted().collect(Collectors.toList()); + } + + public DiagramPoint getFirstPoint(String diagramName) { + return diagramsDetails.getOrDefault(diagramName, Collections.emptyList()).stream().sorted().findFirst().orElse(new DiagramPoint(0, 0, 0)); + } + + public DiagramPoint getLastPoint(String diagramName) { + return diagramsDetails.getOrDefault(diagramName, Collections.emptyList()).stream().sorted(Comparator.reverseOrder()).findFirst().orElse(new DiagramPoint(0, 0, 0)); + } + + public DiagramPoint getFirstPoint(String diagramName, double offset) { + List points = diagramsDetails.getOrDefault(diagramName, Collections.emptyList()); + if (points.size() < 2) { + return getFirstPoint(diagramName); + } + DiagramPoint firstPoint = points.stream().sorted().findFirst().orElseThrow(AssertionError::new); + DiagramPoint secondPoint = points.stream().sorted().skip(1).findFirst().orElseThrow(AssertionError::new); + return shiftPoint(firstPoint, secondPoint, offset); + } + + public DiagramPoint getLastPoint(String diagramName, double offset) { + List points = diagramsDetails.getOrDefault(diagramName, Collections.emptyList()); + if (points.size() < 2) { + return getLastPoint(diagramName); + } + DiagramPoint lastPoint = points.stream().sorted(Comparator.reverseOrder()).findFirst().orElseThrow(AssertionError::new); + DiagramPoint secondLastPoint = points.stream().sorted(Comparator.reverseOrder()).skip(1).findFirst().orElseThrow(AssertionError::new); + return shiftPoint(lastPoint, secondLastPoint, offset); + } + + private DiagramPoint shiftPoint(DiagramPoint point, DiagramPoint otherPoint, double offset) { + Vector2D pointVector = new Vector2D(point.getX(), point.getY()); + Vector2D otherPointVector = new Vector2D(otherPoint.getX(), otherPoint.getY()); + Vector2D shiftedPointVector = pointVector.add(otherPointVector.subtract(pointVector).normalize().scalarMultiply(offset)); + return new DiagramPoint(shiftedPointVector.getX(), shiftedPointVector.getY(), point.getSeq()); + } + + public List getDiagramsNames() { + return new ArrayList<>(diagramsDetails.keySet()); + } + + public static LineDiagramData getOrCreateDiagramData(Line line) { + LineDiagramData lineDiagramData = line.getExtension(LineDiagramData.class); + if (lineDiagramData == null) { + lineDiagramData = new LineDiagramData<>(line); + } + return lineDiagramData; + } + + public static LineDiagramData getOrCreateDiagramData(DanglingLine danglingLine) { + LineDiagramData danglingLineData = danglingLine.getExtension(LineDiagramData.class); + if (danglingLineData == null) { + danglingLineData = new LineDiagramData<>(danglingLine); + } + return danglingLineData; + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/NetworkDiagramData.java b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/NetworkDiagramData.java new file mode 100644 index 000000000..ac42773a1 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/NetworkDiagramData.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.commons.extensions.AbstractExtension; +import com.powsybl.iidm.network.Network; + +import java.util.*; + +/** + * + * @author Christian Biasuzzi + */ +public final class NetworkDiagramData extends AbstractExtension { + + static final String NAME = "network-diagram-data"; + private Set diagramsNames = new TreeSet<>(); + + private NetworkDiagramData() { + } + + private static NetworkDiagramData getNetworkDiagramData(Network network) { + NetworkDiagramData networkDiagramData = network.getExtension(NetworkDiagramData.class); + if (networkDiagramData == null) { + networkDiagramData = new NetworkDiagramData(); + } + return networkDiagramData; + } + + public static void addDiagramName(Network network, String diagramName) { + Objects.requireNonNull(network); + Objects.requireNonNull(diagramName); + NetworkDiagramData networkDiagramData = getNetworkDiagramData(network); + networkDiagramData.addDiagramName(diagramName); + network.addExtension(NetworkDiagramData.class, networkDiagramData); + } + + public static List getDiagramsNames(Network network) { + Objects.requireNonNull(network); + return getNetworkDiagramData(network).getDiagramsNames(); + } + + public static boolean checkNetworkDiagramData(Network network) { + Objects.requireNonNull(network); + return network.getExtension(NetworkDiagramData.class) != null; + } + + public static boolean containsDiagramName(Network network, String diagramName) { + Objects.requireNonNull(network); + Objects.requireNonNull(diagramName); + return checkNetworkDiagramData(network) && getNetworkDiagramData(network).diagramsNames.contains(diagramName); + } + + @Override + public String getName() { + return NAME; + } + + private void addDiagramName(String diagramName) { + diagramsNames.add(diagramName); + } + + private List getDiagramsNames() { + return new ArrayList<>(diagramsNames); + } +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/NodeDiagramData.java b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/NodeDiagramData.java new file mode 100644 index 000000000..b712e02c3 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/NodeDiagramData.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.commons.extensions.AbstractExtension; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.iidm.network.Identifiable; + +import java.util.*; + +/** + * + * @author Massimo Ferraro + */ +public class NodeDiagramData> extends AbstractExtension { + + static final String NAME = "node-diagram-data"; + + public class NodeDiagramDataDetails { + private DiagramPoint point1; + private DiagramPoint point2; + + public DiagramPoint getPoint1() { + return point1; + } + + public void setPoint1(DiagramPoint point1) { + this.point1 = Objects.requireNonNull(point1); + } + + public DiagramPoint getPoint2() { + return point2; + } + + public void setPoint2(DiagramPoint point2) { + this.point2 = Objects.requireNonNull(point2); + } + } + + private Map diagramsDetails = new HashMap<>(); + + private NodeDiagramData(T identifiable) { + super(identifiable); + } + + public NodeDiagramData(BusbarSection busbar) { + this((T) busbar); + } + + public NodeDiagramData(Bus bus) { + this((T) bus); + } + + @Override + public String getName() { + return NAME; + } + + public void addData(String diagramName, NodeDiagramDataDetails nodeDetails) { + Objects.requireNonNull(diagramName); + Objects.requireNonNull(nodeDetails); + diagramsDetails.put(diagramName, nodeDetails); + } + + public NodeDiagramDataDetails getData(String diagramName) { + Objects.requireNonNull(diagramName); + return diagramsDetails.get(diagramName); + } + + public List getDiagramsNames() { + return new ArrayList<>(diagramsDetails.keySet()); + } + + public static NodeDiagramData getOrCreateDiagramData(Bus bus) { + NodeDiagramData nodeDiagramData = bus.getExtension(NodeDiagramData.class); + if (nodeDiagramData == null) { + nodeDiagramData = new NodeDiagramData<>(bus); + } + return nodeDiagramData; + } + + public static NodeDiagramData getOrCreateDiagramData(BusbarSection busbar) { + NodeDiagramData nodeDiagramData = busbar.getExtension(NodeDiagramData.class); + if (nodeDiagramData == null) { + nodeDiagramData = new NodeDiagramData<>(busbar); + } + return nodeDiagramData; + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/ThreeWindingsTransformerDiagramData.java b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/ThreeWindingsTransformerDiagramData.java new file mode 100644 index 000000000..70eda7c8e --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/main/java/com/powsybl/cgmes/iidm/extensions/dl/ThreeWindingsTransformerDiagramData.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.commons.extensions.AbstractExtension; +import com.powsybl.iidm.network.ThreeWindingsTransformer; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * + * @author Massimo Ferraro + */ +public class ThreeWindingsTransformerDiagramData extends AbstractExtension { + + static final String NAME = "three-windings-transformer-diagram-data"; + + public class ThreeWindingsTransformerDiagramDataDetails { + private final DiagramPoint point; + private final double rotation; + private List terminal1Points = new ArrayList<>(); + private List terminal2Points = new ArrayList<>(); + private List terminal3Points = new ArrayList<>(); + + public ThreeWindingsTransformerDiagramDataDetails(DiagramPoint point, double rotation) { + this.point = Objects.requireNonNull(point); + this.rotation = Objects.requireNonNull(rotation); + } + + public void addTerminalPoint(DiagramTerminal terminal, DiagramPoint point) { + Objects.requireNonNull(terminal); + Objects.requireNonNull(point); + switch (terminal) { + case TERMINAL1: + terminal1Points.add(point); + break; + case TERMINAL2: + terminal2Points.add(point); + break; + case TERMINAL3: + terminal3Points.add(point); + break; + default: + throw new AssertionError("Unexpected terminal: " + terminal); + } + } + + public List getTerminalPoints(DiagramTerminal terminal) { + Objects.requireNonNull(terminal); + switch (terminal) { + case TERMINAL1: + return terminal1Points.stream().sorted().collect(Collectors.toList()); + case TERMINAL2: + return terminal2Points.stream().sorted().collect(Collectors.toList()); + case TERMINAL3: + return terminal3Points.stream().sorted().collect(Collectors.toList()); + default: + throw new AssertionError("Unexpected terminal: " + terminal); + } + } + + public DiagramPoint getPoint() { + return point; + } + + public double getRotation() { + return rotation; + } + } + + private Map diagramsDetails = new HashMap<>(); + + public ThreeWindingsTransformerDiagramData(ThreeWindingsTransformer transformer) { + super(transformer); + } + + @Override + public String getName() { + return NAME; + } + + public void addData(String diagramName, ThreeWindingsTransformerDiagramDataDetails nodeDetails) { + Objects.requireNonNull(diagramName); + Objects.requireNonNull(nodeDetails); + diagramsDetails.put(diagramName, nodeDetails); + } + + public ThreeWindingsTransformerDiagramDataDetails getData(String diagramName) { + Objects.requireNonNull(diagramName); + return diagramsDetails.get(diagramName); + } + + public List getDiagramsNames() { + return new ArrayList<>(diagramsDetails.keySet()); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/Networks.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/Networks.java new file mode 100644 index 000000000..e636d20ca --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/Networks.java @@ -0,0 +1,463 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm; + +import org.joda.time.DateTime; + +import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.NetworkFactory; +import com.powsybl.iidm.network.StaticVarCompensator; +import com.powsybl.iidm.network.Substation; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.iidm.network.VscConverterStation; + +/** + * + * @author Massimo Ferraro + */ +public final class Networks { + + private Networks() { + } + + public static Network createNetworkWithBusbar() { + Network network = NetworkFactory.create("Network", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel = substation.newVoltageLevel() + .setId("VoltageLevel") + .setNominalV(400) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + voltageLevel.getNodeBreakerView().setNodeCount(1); + voltageLevel.getNodeBreakerView().newBusbarSection() + .setId("Busbar") + .setNode(0) + .add(); + return network; + } + + public static Network createNetworkWithBus() { + Network network = NetworkFactory.create("Network", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel = substation.newVoltageLevel() + .setId("VoltageLevel") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel.getBusBreakerView().newBus() + .setId("Bus") + .add(); + return network; + } + + public static Network createNetworkWithGenerator() { + Network network = NetworkFactory.create("test", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel = substation.newVoltageLevel() + .setId("VoltageLevel") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel.getBusBreakerView().newBus() + .setId("Bus") + .add(); + voltageLevel.newGenerator() + .setId("Generator") + .setBus("Bus") + .setConnectableBus("Bus") + .setTargetP(100) + .setTargetV(380) + .setVoltageRegulatorOn(true) + .setMaxP(100) + .setMinP(0) + .add(); + return network; + } + + public static Network createNetworkWithLine() { + Network network = NetworkFactory.create("Network", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation1 = network.newSubstation() + .setId("Substation1") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel1 = substation1.newVoltageLevel() + .setId("VoltageLevel1") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel1.getBusBreakerView().newBus() + .setId("Bus1") + .add(); + Substation substation2 = network.newSubstation() + .setId("Substation2") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel2 = substation2.newVoltageLevel() + .setId("VoltageLevel2") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel2.getBusBreakerView().newBus() + .setId("Bus2") + .add(); + network.newLine() + .setId("Line") + .setVoltageLevel1(voltageLevel1.getId()) + .setBus1("Bus1") + .setConnectableBus1("Bus1") + .setVoltageLevel2(voltageLevel2.getId()) + .setBus2("Bus2") + .setConnectableBus2("Bus2") + .setR(3.0) + .setX(33.0) + .setG1(0.0) + .setB1(386E-6 / 2) + .setG2(0.0) + .setB2(386E-6 / 2) + .add(); + return network; + } + + public static Network createNetworkWithLoad() { + Network network = NetworkFactory.create("test", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel = substation.newVoltageLevel() + .setId("VoltageLevel") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel.getBusBreakerView().newBus() + .setId("Bus") + .add(); + voltageLevel.newLoad() + .setId("Load") + .setBus("Bus") + .setConnectableBus("Bus") + .setP0(100) + .setQ0(50) + .add(); + return network; + } + + public static Network createNetworkWithShuntCompensator() { + Network network = NetworkFactory.create("test", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel = substation.newVoltageLevel() + .setId("VoltageLevel") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel.getBusBreakerView().newBus() + .setId("Bus") + .add(); + voltageLevel.newShuntCompensator() + .setId("Shunt") + .setBus("Bus") + .setConnectableBus("Bus") + .setbPerSection(1e-5) + .setCurrentSectionCount(1) + .setMaximumSectionCount(1) + .add(); + return network; + } + + public static Network createNetworkWithStaticVarCompensator() { + Network network = NetworkFactory.create("test", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel = substation.newVoltageLevel() + .setId("VoltageLevel") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel.getBusBreakerView().newBus() + .setId("Bus") + .add(); + voltageLevel.newStaticVarCompensator() + .setId("Svc") + .setConnectableBus("Bus") + .setBus("Bus") + .setBmin(0.0002) + .setBmax(0.0008) + .setRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE) + .setVoltageSetPoint(390.0) + .setReactivePowerSetPoint(1.0) + .add(); + return network; + } + + public static Network createNetworkWithSwitch() { + Network network = NetworkFactory.create("Network", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel = substation.newVoltageLevel() + .setId("VoltageLevel") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel.getBusBreakerView().newBus() + .setId("Bus1") + .add(); + voltageLevel.getBusBreakerView().newBus() + .setId("Bus2") + .add(); + voltageLevel.getBusBreakerView().newSwitch() + .setId("Switch") + .setBus1("Bus1") + .setBus2("Bus1") + .setOpen(false) + .add(); + return network; + } + + public static Network createNetworkWithThreeWindingsTransformer() { + Network network = NetworkFactory.create("Network", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel1 = substation.newVoltageLevel() + .setId("VoltageLevel1") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel1.getBusBreakerView().newBus() + .setId("Bus1") + .add(); + VoltageLevel voltageLevel2 = substation.newVoltageLevel() + .setId("VoltageLevel2") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel2.getBusBreakerView().newBus() + .setId("Bus2") + .add(); + VoltageLevel voltageLevel3 = substation.newVoltageLevel() + .setId("VoltageLevel3") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel3.getBusBreakerView().newBus() + .setId("Bus3") + .add(); + substation.newThreeWindingsTransformer() + .setId("Transformer3w") + .newLeg1() + .setR(17.424) + .setX(1.7424) + .setB(0.000573921028466483) + .setG(0.00573921028466483) + .setRatedU(132.0) + .setVoltageLevel(voltageLevel1.getId()) + .setBus("Bus1") + .add() + .newLeg2() + .setR(1.089) + .setX(0.1089) + .setRatedU(33.0) + .setVoltageLevel(voltageLevel2.getId()) + .setBus("Bus2") + .add() + .newLeg3() + .setR(0.121) + .setX(0.0121) + .setRatedU(11.0) + .setVoltageLevel(voltageLevel3.getId()) + .setBus("Bus3") + .add() + .add(); + return network; + } + + public static Network createNetworkWithTwoWindingsTransformer() { + Network network = NetworkFactory.create("Network", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel1 = substation.newVoltageLevel() + .setId("VoltageLevel1") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel1.getBusBreakerView().newBus() + .setId("Bus1") + .add(); + VoltageLevel voltageLevel2 = substation.newVoltageLevel() + .setId("VoltageLevel2") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel2.getBusBreakerView().newBus() + .setId("Bus2") + .add(); + int zb380 = 380 * 380 / 100; + substation.newTwoWindingsTransformer() + .setId("Transformer") + .setVoltageLevel1(voltageLevel1.getId()) + .setBus1("Bus1") + .setConnectableBus1("Bus1") + .setRatedU1(24.0) + .setVoltageLevel2(voltageLevel2.getId()) + .setBus2("Bus2") + .setConnectableBus2("Bus2") + .setRatedU2(400.0) + .setR(0.24 / 1300 * zb380) + .setX(Math.sqrt(10 * 10 - 0.24 * 0.24) / 1300 * zb380) + .setG(0.0) + .setB(0.0) + .add(); + return network; + } + + public static Network createNetworkWithDanglingLine() { + Network network = NetworkFactory.create("Network", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel = substation.newVoltageLevel() + .setId("VoltageLevel") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel.getBusBreakerView().newBus() + .setId("Bus") + .add(); + voltageLevel.newDanglingLine() + .setId("DanglingLine") + .setBus("Bus") + .setR(10.0) + .setX(1.0) + .setB(10e-6) + .setG(10e-5) + .setP0(50.0) + .setQ0(30.0) + .add(); + return network; + } + + public static Network createNetworkWithHvdcLine() { + Network network = NetworkFactory.create("Network", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation substation1 = network.newSubstation() + .setId("Substation1") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel1 = substation1.newVoltageLevel() + .setId("VoltageLevel1") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel1.getBusBreakerView().newBus() + .setId("Bus1") + .add(); + VscConverterStation cs1 = voltageLevel1.newVscConverterStation() + .setId("Converter1") + .setConnectableBus("Bus1") + .setBus("Bus1") + .setLossFactor(0.011f) + .setVoltageSetpoint(405.0) + .setVoltageRegulatorOn(true) + .add(); + Substation substation2 = network.newSubstation() + .setId("Substation2") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel2 = substation2.newVoltageLevel() + .setId("VoltageLevel2") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel2.getBusBreakerView().newBus() + .setId("Bus2") + .add(); + VscConverterStation cs2 = voltageLevel2.newVscConverterStation() + .setId("Converter2") + .setConnectableBus("Bus2") + .setBus("Bus2") + .setLossFactor(0.011f) + .setReactivePowerSetpoint(123) + .setVoltageRegulatorOn(false) + .add(); + network.newHvdcLine() + .setId("HvdcLine") + .setConverterStationId1("Converter1") + .setConverterStationId2("Converter2") + .setR(1) + .setNominalV(400) + .setConvertersMode(HvdcLine.ConvertersMode.SIDE_1_INVERTER_SIDE_2_RECTIFIER) + .setMaxP(300.0) + .setActivePowerSetpoint(280) + .add(); + return network; + } + + public static Network createNetworkWithBusbarAndSwitch() { + Network network = Network.create("Network", "test"); + network.setCaseDate(DateTime.parse("2018-01-01T00:30:00.000+01:00")); + Substation s = network.newSubstation() + .setId("S") + .setCountry(Country.FR) + .add(); + VoltageLevel vl = s.newVoltageLevel() + .setId("VL") + .setNominalV(400.0) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + vl.getNodeBreakerView().setNodeCount(10); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS1") + .setNode(0) + .add(); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS2") + .setNode(1) + .add(); + vl.getNodeBreakerView().newBreaker() + .setId("B") + .setNode1(0) + .setNode2(1) + .setOpen(false) + .setRetained(true) + .add(); + return network; + } +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractCouplingDeviceDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractCouplingDeviceDiagramDataTest.java new file mode 100644 index 000000000..58190dcb9 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractCouplingDeviceDiagramDataTest.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractCouplingDeviceDiagramDataTest { + protected static String DIAGRAM_NAME = "default"; + + protected void checkDiagramData(CouplingDeviceDiagramData diagramData, String diagramName) { + assertNotNull(diagramData); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails dataDetails = diagramData.getData(diagramName); + assertEquals(0, dataDetails.getPoint().getSeq(), 0); + assertEquals(20, dataDetails.getPoint().getX(), 0); + assertEquals(10, dataDetails.getPoint().getY(), 0); + assertEquals(90, dataDetails.getRotation(), 0); + List t1Points = dataDetails.getTerminalPoints(DiagramTerminal.TERMINAL1); + List t2Points = dataDetails.getTerminalPoints(DiagramTerminal.TERMINAL2); + assertEquals(1, t1Points.get(0).getSeq(), 0); + assertEquals(0, t1Points.get(0).getX(), 0); + assertEquals(10, t1Points.get(0).getY(), 0); + assertEquals(2, t1Points.get(1).getSeq(), 0); + assertEquals(15, t1Points.get(1).getX(), 0); + assertEquals(10, t1Points.get(1).getY(), 0); + assertEquals(1, t2Points.get(0).getSeq(), 0); + assertEquals(25, t2Points.get(0).getX(), 0); + assertEquals(10, t2Points.get(0).getY(), 0); + assertEquals(2, t2Points.get(1).getSeq(), 0); + assertEquals(40, t2Points.get(1).getX(), 0); + assertEquals(10, t2Points.get(1).getY(), 0); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractInjectionDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractInjectionDiagramDataTest.java new file mode 100644 index 000000000..acf1863e6 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractInjectionDiagramDataTest.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractInjectionDiagramDataTest { + protected static String DIAGRAM_NAME = "default"; + + protected void checkDiagramData(InjectionDiagramData diagramData, String diagramName) { + assertNotNull(diagramData); + InjectionDiagramData.InjectionDiagramDetails diagramDataDetails = diagramData.getData(diagramName); + assertEquals(0, diagramDataDetails.getPoint().getSeq(), 0); + assertEquals(20, diagramDataDetails.getPoint().getX(), 0); + assertEquals(10, diagramDataDetails.getPoint().getY(), 0); + assertEquals(90, diagramDataDetails.getRotation(), 0); + List points = diagramDataDetails.getTerminalPoints(); + assertEquals(1, points.get(0).getSeq(), 0); + assertEquals(0, points.get(0).getX(), 0); + assertEquals(10, points.get(0).getY(), 0); + assertEquals(2, points.get(1).getSeq(), 0); + assertEquals(15, points.get(1).getX(), 0); + assertEquals(10, points.get(1).getY(), 0); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractLineDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractLineDiagramDataTest.java new file mode 100644 index 000000000..d153dc033 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractLineDiagramDataTest.java @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractLineDiagramDataTest { + protected static final String DIAGRAM_NAME = "default"; + protected static final String DIAGRAM2_NAME = "diagram2"; + + protected void checkDiagramData(LineDiagramData diagramData, String diagramName) { + assertNotNull(diagramData); + assertNotNull(diagramName); + switch (diagramName) { + case DIAGRAM_NAME: + assertEquals(1, diagramData.getPoints(diagramName).get(0).getSeq(), 0); + assertEquals(0, diagramData.getPoints(diagramName).get(0).getX(), 0); + assertEquals(10, diagramData.getPoints(diagramName).get(0).getY(), 0); + assertEquals(2, diagramData.getPoints(diagramName).get(1).getSeq(), 0); + assertEquals(10, diagramData.getPoints(diagramName).get(1).getX(), 0); + assertEquals(0, diagramData.getPoints(diagramName).get(1).getY(), 0); + assertEquals(1, diagramData.getFirstPoint(diagramName).getSeq(), 0); + assertEquals(0, diagramData.getFirstPoint(diagramName).getX(), 0); + assertEquals(10, diagramData.getFirstPoint(diagramName).getY(), 0); + assertEquals(2, diagramData.getLastPoint(diagramName).getSeq(), 0); + assertEquals(10, diagramData.getLastPoint(diagramName).getX(), 0); + assertEquals(0, diagramData.getLastPoint(diagramName).getY(), 0); + assertEquals(1, diagramData.getFirstPoint(diagramName, 5).getSeq(), 0); + assertEquals(3.535, diagramData.getFirstPoint(diagramName, 5).getX(), .001); + assertEquals(6.464, diagramData.getFirstPoint(diagramName, 5).getY(), .001); + assertEquals(2, diagramData.getLastPoint(diagramName, 5).getSeq(), 0); + assertEquals(6.464, diagramData.getLastPoint(diagramName, 5).getX(), .001); + assertEquals(3.535, diagramData.getLastPoint(diagramName, 5).getY(), .001); + break; + case DIAGRAM2_NAME: + assertEquals(10, diagramData.getPoints(diagramName).get(0).getX(), 0); + assertEquals(20, diagramData.getPoints(diagramName).get(0).getY(), 0); + assertEquals(1, diagramData.getPoints(diagramName).get(0).getSeq(), 0); + assertEquals(20, diagramData.getPoints(diagramName).get(1).getX(), 0); + assertEquals(10, diagramData.getPoints(diagramName).get(1).getY(), 0); + assertEquals(2, diagramData.getPoints(diagramName).get(1).getSeq(), 0); + assertEquals(10, diagramData.getFirstPoint(diagramName).getX(), 0); + assertEquals(20, diagramData.getFirstPoint(diagramName).getY(), 0); + assertEquals(1, diagramData.getFirstPoint(diagramName).getSeq(), 0); + assertEquals(20, diagramData.getLastPoint(diagramName).getX(), 0); + assertEquals(10, diagramData.getLastPoint(diagramName).getY(), 0); + assertEquals(2, diagramData.getLastPoint(diagramName).getSeq(), 0); + assertEquals(1, diagramData.getFirstPoint(diagramName, 5).getSeq(), 0); + assertEquals(13.535, diagramData.getFirstPoint(diagramName, 5).getX(), .001); + assertEquals(16.464, diagramData.getFirstPoint(diagramName, 5).getY(), .001); + assertEquals(2, diagramData.getLastPoint(diagramName, 5).getSeq(), 0); + assertEquals(16.464, diagramData.getLastPoint(diagramName, 5).getX(), .001); + assertEquals(13.535, diagramData.getLastPoint(diagramName, 5).getY(), .001); + break; + } + + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractNodeDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractNodeDiagramDataTest.java new file mode 100644 index 000000000..a4e8ab957 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/AbstractNodeDiagramDataTest.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractNodeDiagramDataTest { + protected static String DIAGRAM_NAME = "default"; + + protected void checkDiagramData(NodeDiagramData diagramData, String diagramName) { + assertNotNull(diagramData); + NodeDiagramData.NodeDiagramDataDetails nodeDetails = diagramData.getData(diagramName); + assertNotNull(nodeDetails); + assertEquals(1, nodeDetails.getPoint1().getSeq(), 0); + assertEquals(0, nodeDetails.getPoint1().getX(), 0); + assertEquals(10, nodeDetails.getPoint1().getY(), 0); + assertEquals(2, nodeDetails.getPoint2().getSeq(), 0); + assertEquals(10, nodeDetails.getPoint2().getX(), 0); + assertEquals(0, nodeDetails.getPoint2().getY(), 0); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/BusDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/BusDiagramDataTest.java new file mode 100644 index 000000000..27d192cb2 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/BusDiagramDataTest.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Network; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * + * @author Massimo Ferraro + */ +public class BusDiagramDataTest extends AbstractNodeDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithBus(); + Bus bus = network.getVoltageLevel("VoltageLevel").getBusBreakerView().getBus("Bus"); + + NodeDiagramData busDiagramData = NodeDiagramData.getOrCreateDiagramData(bus); + assertNotNull(busDiagramData); + NodeDiagramData.NodeDiagramDataDetails busDiagramDetails = busDiagramData.new NodeDiagramDataDetails(); + + busDiagramDetails.setPoint1(new DiagramPoint(0, 10, 1)); + busDiagramDetails.setPoint2(new DiagramPoint(10, 0, 2)); + busDiagramData.addData(DIAGRAM_NAME, busDiagramDetails); + assertTrue(busDiagramData.getDiagramsNames().size() > 0); + + bus.addExtension(NodeDiagramData.class, busDiagramData); + + Bus bus2 = network.getVoltageLevel("VoltageLevel").getBusBreakerView().getBus("Bus"); + NodeDiagramData busDiagramData2 = bus2.getExtension(NodeDiagramData.class); + + checkDiagramData(busDiagramData2, DIAGRAM_NAME); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/BusbarDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/BusbarDiagramDataTest.java new file mode 100644 index 000000000..7a09f5f4d --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/BusbarDiagramDataTest.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.iidm.network.Network; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + +/** + * + * @author Massimo Ferraro + */ +public class BusbarDiagramDataTest extends AbstractNodeDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithBusbar(); + BusbarSection busbar = network.getVoltageLevel("VoltageLevel").getNodeBreakerView().getBusbarSection("Busbar"); + + NodeDiagramData busbarDiagramData = NodeDiagramData.getOrCreateDiagramData(busbar); + assertNotNull(busbarDiagramData); + NodeDiagramData.NodeDiagramDataDetails nodeDetails = busbarDiagramData.new NodeDiagramDataDetails(); + nodeDetails.setPoint2(new DiagramPoint(10, 0, 2)); + nodeDetails.setPoint1(new DiagramPoint(0, 10, 1)); + busbarDiagramData.addData(DIAGRAM_NAME, nodeDetails); + busbar.addExtension(NodeDiagramData.class, busbarDiagramData); + + BusbarSection busbar2 = network.getVoltageLevel("VoltageLevel").getNodeBreakerView().getBusbarSection("Busbar"); + NodeDiagramData busbarDiagramData2 = busbar2.getExtension(NodeDiagramData.class); + + checkDiagramData(busbarDiagramData2, DIAGRAM_NAME); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/DanglingLineDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/DanglingLineDiagramDataTest.java new file mode 100644 index 000000000..737cbc252 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/DanglingLineDiagramDataTest.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.Network; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * + * @author Massimo Ferraro + */ +public class DanglingLineDiagramDataTest extends AbstractLineDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithDanglingLine(); + DanglingLine danglingLine = network.getDanglingLine("DanglingLine"); + + LineDiagramData danglingLineDiagramData = LineDiagramData.getOrCreateDiagramData(danglingLine); + assertNotNull(danglingLineDiagramData); + + danglingLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(10, 0, 2)); + danglingLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(0, 10, 1)); + danglingLine.addExtension(LineDiagramData.class, danglingLineDiagramData); + + DanglingLine danglingLine2 = network.getDanglingLine("DanglingLine"); + LineDiagramData danglingLineDiagramData2 = danglingLine2.getExtension(LineDiagramData.class); + + assertEquals(1, danglingLineDiagramData2.getDiagramsNames().size()); + checkDiagramData(danglingLineDiagramData2, DIAGRAM_NAME); + } + + @Test + public void testMultipleDiagrams() { + Network network = Networks.createNetworkWithDanglingLine(); + DanglingLine danglingLine = network.getDanglingLine("DanglingLine"); + + LineDiagramData danglingLineDiagramData = LineDiagramData.getOrCreateDiagramData(danglingLine); + assertNotNull(danglingLineDiagramData); + + danglingLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(10, 0, 2)); + danglingLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(0, 10, 1)); + danglingLineDiagramData.addPoint(DIAGRAM2_NAME, new DiagramPoint(10, 20, 1)); + danglingLineDiagramData.addPoint(DIAGRAM2_NAME, new DiagramPoint(20, 10, 2)); + danglingLine.addExtension(LineDiagramData.class, danglingLineDiagramData); + + DanglingLine danglingLine2 = network.getDanglingLine("DanglingLine"); + LineDiagramData danglingLineDiagramData2 = danglingLine2.getExtension(LineDiagramData.class); + + assertEquals(2, danglingLineDiagramData2.getDiagramsNames().size()); + checkDiagramData(danglingLineDiagramData2, DIAGRAM_NAME); + checkDiagramData(danglingLineDiagramData2, DIAGRAM2_NAME); + } +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/GeneratorDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/GeneratorDiagramDataTest.java new file mode 100644 index 000000000..fa345268c --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/GeneratorDiagramDataTest.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Network; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** + * + * @author Massimo Ferraro + */ +public class GeneratorDiagramDataTest extends AbstractInjectionDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithGenerator(); + Generator generator = network.getGenerator("Generator"); + + InjectionDiagramData generatorDiagramData = new InjectionDiagramData<>(generator); + InjectionDiagramData.InjectionDiagramDetails diagramDataDetails = generatorDiagramData.new InjectionDiagramDetails(new DiagramPoint(20, 10, 0), 90); + diagramDataDetails.addTerminalPoint(new DiagramPoint(15, 10, 2)); + diagramDataDetails.addTerminalPoint(new DiagramPoint(0, 10, 1)); + generatorDiagramData.addData(DIAGRAM_NAME, diagramDataDetails); + generator.addExtension(InjectionDiagramData.class, generatorDiagramData); + assertTrue(generatorDiagramData.getDiagramsNames().size() > 0); + + Generator generator2 = network.getGenerator("Generator"); + InjectionDiagramData generatorDiagramData2 = generator2.getExtension(InjectionDiagramData.class); + + checkDiagramData(generatorDiagramData2, DIAGRAM_NAME); + + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/HvdcLineDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/HvdcLineDiagramDataTest.java new file mode 100644 index 000000000..a03141031 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/HvdcLineDiagramDataTest.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import org.junit.Test; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.HvdcLine; +import com.powsybl.iidm.network.Network; + +import static org.junit.Assert.assertEquals; + +/** + * + * @author Massimo Ferraro + */ +public class HvdcLineDiagramDataTest extends AbstractLineDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithHvdcLine(); + HvdcLine hvdcLine = network.getHvdcLine("HvdcLine"); + + LineDiagramData hvdcLineDiagramData = new LineDiagramData<>(hvdcLine); + hvdcLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(10, 0, 2)); + hvdcLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(0, 10, 1)); + hvdcLine.addExtension(LineDiagramData.class, hvdcLineDiagramData); + + HvdcLine hvdcLine2 = network.getHvdcLine("HvdcLine"); + LineDiagramData hvdcLineDiagramData2 = hvdcLine2.getExtension(LineDiagramData.class); + + assertEquals(1, hvdcLineDiagramData2.getDiagramsNames().size()); + checkDiagramData(hvdcLineDiagramData2, DIAGRAM_NAME); + } + + @Test + public void testMultipleDiagrams() { + Network network = Networks.createNetworkWithHvdcLine(); + HvdcLine hvdcLine = network.getHvdcLine("HvdcLine"); + + LineDiagramData hvdcLineDiagramData = new LineDiagramData<>(hvdcLine); + hvdcLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(10, 0, 2)); + hvdcLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(0, 10, 1)); + hvdcLineDiagramData.addPoint(DIAGRAM2_NAME, new DiagramPoint(10, 20, 1)); + hvdcLineDiagramData.addPoint(DIAGRAM2_NAME, new DiagramPoint(20, 10, 2)); + hvdcLine.addExtension(LineDiagramData.class, hvdcLineDiagramData); + + HvdcLine hvdcLine2 = network.getHvdcLine("HvdcLine"); + LineDiagramData hvdcLineDiagramData2 = hvdcLine2.getExtension(LineDiagramData.class); + + assertEquals(2, hvdcLineDiagramData2.getDiagramsNames().size()); + checkDiagramData(hvdcLineDiagramData2, DIAGRAM_NAME); + checkDiagramData(hvdcLineDiagramData2, DIAGRAM2_NAME); + } +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/LineDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/LineDiagramDataTest.java new file mode 100644 index 000000000..0bd315942 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/LineDiagramDataTest.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Network; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + * @author Massimo Ferraro + */ +public class LineDiagramDataTest extends AbstractLineDiagramDataTest { + + protected static final String DIAGRAM2_NAME = "diagram2"; + + @Test + public void test() { + Network network = Networks.createNetworkWithLine(); + Line line = network.getLine("Line"); + + LineDiagramData lineDiagramData = LineDiagramData.getOrCreateDiagramData(line); + assertNotNull(lineDiagramData); + lineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(10, 0, 2)); + lineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(0, 10, 1)); + line.addExtension(LineDiagramData.class, lineDiagramData); + + Line line2 = network.getLine("Line"); + LineDiagramData lineDiagramData2 = line2.getExtension(LineDiagramData.class); + + assertTrue(lineDiagramData2.getDiagramsNames().size() == 1); + checkDiagramData(lineDiagramData2, DIAGRAM_NAME); + } + + @Test + public void testMultipleDiagrams() { + Network network = Networks.createNetworkWithLine(); + Line line = network.getLine("Line"); + + LineDiagramData lineDiagramData = LineDiagramData.getOrCreateDiagramData(line); + assertNotNull(lineDiagramData); + lineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(10, 0, 2)); + lineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(0, 10, 1)); + + lineDiagramData.addPoint(DIAGRAM2_NAME, new DiagramPoint(10, 20, 1)); + lineDiagramData.addPoint(DIAGRAM2_NAME, new DiagramPoint(20, 10, 2)); + + line.addExtension(LineDiagramData.class, lineDiagramData); + + Line line2 = network.getLine("Line"); + LineDiagramData lineDiagramData2 = line2.getExtension(LineDiagramData.class); + + assertEquals(2, lineDiagramData2.getDiagramsNames().size()); + checkDiagramData(lineDiagramData2, DIAGRAM_NAME); + checkDiagramData(lineDiagramData2, DIAGRAM2_NAME); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/LoadDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/LoadDiagramDataTest.java new file mode 100644 index 000000000..6547f50d6 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/LoadDiagramDataTest.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import org.junit.Test; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; + +/** + * + * @author Massimo Ferraro + */ +public class LoadDiagramDataTest extends AbstractInjectionDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithLoad(); + Load load = network.getLoad("Load"); + + InjectionDiagramData loadDiagramData = new InjectionDiagramData<>(load); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = loadDiagramData.new InjectionDiagramDetails(new DiagramPoint(20, 10, 0), 90); + diagramDetails.addTerminalPoint(new DiagramPoint(15, 10, 2)); + diagramDetails.addTerminalPoint(new DiagramPoint(0, 10, 1)); + loadDiagramData.addData(DIAGRAM_NAME, diagramDetails); + load.addExtension(InjectionDiagramData.class, loadDiagramData); + + Load load2 = network.getLoad("Load"); + InjectionDiagramData loadDiagramData2 = load2.getExtension(InjectionDiagramData.class); + + checkDiagramData(loadDiagramData2, DIAGRAM_NAME); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/NetworkDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/NetworkDiagramDataTest.java new file mode 100644 index 000000000..a30f41a89 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/NetworkDiagramDataTest.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Network; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + * @author Christian Biasuzzi + */ +public class NetworkDiagramDataTest { + + protected static String DIAGRAM_NAME = "diagram"; + protected static String DIAGRAM_NAME2 = "diagram2"; + + @Test + public void test() { + Network network = Networks.createNetworkWithGenerator(); + assertFalse(NetworkDiagramData.checkNetworkDiagramData(network)); + assertEquals(0, NetworkDiagramData.getDiagramsNames(network).size()); + assertFalse(NetworkDiagramData.containsDiagramName(network, DIAGRAM_NAME)); + + NetworkDiagramData.addDiagramName(network, DIAGRAM_NAME); + assertEquals(1, NetworkDiagramData.getDiagramsNames(network).size()); + assertEquals(DIAGRAM_NAME, NetworkDiagramData.getDiagramsNames(network).get(0)); + assertNotEquals(DIAGRAM_NAME2, NetworkDiagramData.getDiagramsNames(network).get(0)); + + Network network2 = Networks.createNetworkWithGenerator(); + assertEquals(0, NetworkDiagramData.getDiagramsNames(network2).size()); + assertEquals(1, NetworkDiagramData.getDiagramsNames(network).size()); + + assertTrue(NetworkDiagramData.checkNetworkDiagramData(network)); + assertTrue(NetworkDiagramData.containsDiagramName(network, DIAGRAM_NAME)); + assertFalse(NetworkDiagramData.containsDiagramName(network, DIAGRAM_NAME2)); + + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/ShuntDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/ShuntDiagramDataTest.java new file mode 100644 index 000000000..5659cc4f7 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/ShuntDiagramDataTest.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import org.junit.Test; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ShuntCompensator; + +/** + * + * @author Massimo Ferraro + */ +public class ShuntDiagramDataTest extends AbstractInjectionDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithShuntCompensator(); + ShuntCompensator shunt = network.getShuntCompensator("Shunt"); + + InjectionDiagramData shuntDiagramData = new InjectionDiagramData<>(shunt); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = shuntDiagramData.new InjectionDiagramDetails(new DiagramPoint(20, 10, 0), 90); + diagramDetails.addTerminalPoint(new DiagramPoint(15, 10, 2)); + diagramDetails.addTerminalPoint(new DiagramPoint(0, 10, 1)); + shuntDiagramData.addData(DIAGRAM_NAME, diagramDetails); + shunt.addExtension(InjectionDiagramData.class, shuntDiagramData); + + ShuntCompensator shunt2 = network.getShuntCompensator("Shunt"); + InjectionDiagramData shuntDiagramData2 = shunt2.getExtension(InjectionDiagramData.class); + + checkDiagramData(shuntDiagramData2, DIAGRAM_NAME); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/StaticVarCompensatorDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/StaticVarCompensatorDiagramDataTest.java new file mode 100644 index 000000000..bb40904d3 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/StaticVarCompensatorDiagramDataTest.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import org.junit.Test; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.StaticVarCompensator; + +/** + * + * @author Massimo Ferraro + */ +public class StaticVarCompensatorDiagramDataTest extends AbstractInjectionDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithStaticVarCompensator(); + StaticVarCompensator svc = network.getStaticVarCompensator("Svc"); + + InjectionDiagramData svcDiagramData = new InjectionDiagramData<>(svc); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = svcDiagramData.new InjectionDiagramDetails(new DiagramPoint(20, 10, 0), 90); + diagramDetails.addTerminalPoint(new DiagramPoint(15, 10, 2)); + diagramDetails.addTerminalPoint(new DiagramPoint(0, 10, 1)); + svcDiagramData.addData(DIAGRAM_NAME, diagramDetails); + svc.addExtension(InjectionDiagramData.class, svcDiagramData); + + StaticVarCompensator svc2 = network.getStaticVarCompensator("Svc"); + InjectionDiagramData svcDiagramData2 = svc2.getExtension(InjectionDiagramData.class); + + checkDiagramData(svcDiagramData2, DIAGRAM_NAME); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/SwitchDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/SwitchDiagramDataTest.java new file mode 100644 index 000000000..caac93d3f --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/SwitchDiagramDataTest.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Switch; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** + * + * @author Massimo Ferraro + */ +public class SwitchDiagramDataTest extends AbstractCouplingDeviceDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithSwitch(); + Switch sw = network.getSwitch("Switch"); + + CouplingDeviceDiagramData switchDiagramData = new CouplingDeviceDiagramData<>(sw); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails diagramDetails = switchDiagramData.new CouplingDeviceDiagramDetails(new DiagramPoint(20, 10, 0), 90); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, new DiagramPoint(15, 10, 2)); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, new DiagramPoint(0, 10, 1)); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, new DiagramPoint(25, 10, 1)); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, new DiagramPoint(40, 10, 2)); + switchDiagramData.addData(DIAGRAM_NAME, diagramDetails); + sw.addExtension(CouplingDeviceDiagramData.class, switchDiagramData); + assertTrue(switchDiagramData.getDiagramsNames().size() > 0); + + Switch sw2 = network.getSwitch("Switch"); + CouplingDeviceDiagramData switchDiagramData2 = sw2.getExtension(CouplingDeviceDiagramData.class); + + checkDiagramData(switchDiagramData2, DIAGRAM_NAME); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/ThreeWindingsTransformerDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/ThreeWindingsTransformerDiagramDataTest.java new file mode 100644 index 000000000..329ed0ab5 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/ThreeWindingsTransformerDiagramDataTest.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + * @author Massimo Ferraro + */ +public class ThreeWindingsTransformerDiagramDataTest { + + public static String DIAGRAM_NAME = "default"; + + @Test + public void test() { + Network network = Networks.createNetworkWithThreeWindingsTransformer(); + ThreeWindingsTransformer twt = network.getThreeWindingsTransformer("Transformer3w"); + + ThreeWindingsTransformerDiagramData twtDiagramData = new ThreeWindingsTransformerDiagramData(twt); + ThreeWindingsTransformerDiagramData.ThreeWindingsTransformerDiagramDataDetails diagramDetails = twtDiagramData.new ThreeWindingsTransformerDiagramDataDetails(new DiagramPoint(20, 13, 0), 90); + + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, new DiagramPoint(15, 10, 2)); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, new DiagramPoint(0, 10, 1)); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, new DiagramPoint(25, 10, 1)); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, new DiagramPoint(40, 10, 2)); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL3, new DiagramPoint(20, 16, 1)); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL3, new DiagramPoint(20, 30, 2)); + + twtDiagramData.addData(DIAGRAM_NAME, diagramDetails); + twt.addExtension(ThreeWindingsTransformerDiagramData.class, twtDiagramData); + assertTrue(twtDiagramData.getDiagramsNames().size() > 0); + + ThreeWindingsTransformer twt2 = network.getThreeWindingsTransformer("Transformer3w"); + ThreeWindingsTransformerDiagramData twtDiagramData2 = twt2.getExtension(ThreeWindingsTransformerDiagramData.class); + assertNotNull(twtDiagramData2); + + ThreeWindingsTransformerDiagramData.ThreeWindingsTransformerDiagramDataDetails diagramDataDetails2 = twtDiagramData2.getData(DIAGRAM_NAME); + assertNotNull(diagramDataDetails2); + + assertEquals(0, diagramDataDetails2.getPoint().getSeq(), 0); + assertEquals(20, diagramDataDetails2.getPoint().getX(), 0); + assertEquals(13, diagramDataDetails2.getPoint().getY(), 0); + assertEquals(90, diagramDataDetails2.getRotation(), 0); + assertEquals(1, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL1).get(0).getSeq(), 0); + assertEquals(0, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL1).get(0).getX(), 0); + assertEquals(10, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL1).get(0).getY(), 0); + assertEquals(2, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL1).get(1).getSeq(), 0); + assertEquals(15, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL1).get(1).getX(), 0); + assertEquals(10, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL1).get(1).getY(), 0); + assertEquals(1, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL2).get(0).getSeq(), 0); + assertEquals(25, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL2).get(0).getX(), 0); + assertEquals(10, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL2).get(0).getY(), 0); + assertEquals(2, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL2).get(1).getSeq(), 0); + assertEquals(40, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL2).get(1).getX(), 0); + assertEquals(10, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL2).get(1).getY(), 0); + assertEquals(1, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL3).get(0).getSeq(), 0); + assertEquals(20, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL3).get(0).getX(), 0); + assertEquals(16, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL3).get(0).getY(), 0); + assertEquals(2, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL3).get(1).getSeq(), 0); + assertEquals(20, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL3).get(1).getX(), 0); + assertEquals(30, diagramDataDetails2.getTerminalPoints(DiagramTerminal.TERMINAL3).get(1).getY(), 0); + } + +} diff --git a/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/TransformerDiagramDataTest.java b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/TransformerDiagramDataTest.java new file mode 100644 index 000000000..91549c4d6 --- /dev/null +++ b/cgmes-dl/cgmes-iidm-extensions/src/test/java/com/powsybl/cgmes/iidm/extensions/dl/TransformerDiagramDataTest.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.cgmes.iidm.extensions.dl; + +import org.junit.Test; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +/** + * + * @author Massimo Ferraro + */ +public class TransformerDiagramDataTest extends AbstractCouplingDeviceDiagramDataTest { + + @Test + public void test() { + Network network = Networks.createNetworkWithTwoWindingsTransformer(); + TwoWindingsTransformer twt = network.getTwoWindingsTransformer("Transformer"); + + CouplingDeviceDiagramData twtDiagramData = new CouplingDeviceDiagramData<>(twt); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails twtDiagramDataDetails = twtDiagramData.new CouplingDeviceDiagramDetails(new DiagramPoint(20, 10, 0), 90); + + twtDiagramDataDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, new DiagramPoint(15, 10, 2)); + twtDiagramDataDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, new DiagramPoint(0, 10, 1)); + twtDiagramDataDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, new DiagramPoint(25, 10, 1)); + twtDiagramDataDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, new DiagramPoint(40, 10, 2)); + + twtDiagramData.addData(DIAGRAM_NAME, twtDiagramDataDetails); + twt.addExtension(CouplingDeviceDiagramData.class, twtDiagramData); + + TwoWindingsTransformer twt2 = network.getTwoWindingsTransformer("Transformer"); + CouplingDeviceDiagramData twtDiagramData2 = twt2.getExtension(CouplingDeviceDiagramData.class); + + checkDiagramData(twtDiagramData2, DIAGRAM_NAME); + } + +} diff --git a/cgmes-dl/pom.xml b/cgmes-dl/pom.xml new file mode 100644 index 000000000..cfcc3a65a --- /dev/null +++ b/cgmes-dl/pom.xml @@ -0,0 +1,79 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-single-line-diagram + 1.0.0-SNAPSHOT + + + pom + powsybl-cgmes-dl + CGMES DL + + + cgmes-dl-conversion + cgmes-iidm-extensions + + + + + + com.powsybl + powsybl-cgmes-conversion + ${powsyblcore.version} + + + com.powsybl + powsybl-cgmes-model + ${powsyblcore.version} + + + com.powsybl + powsybl-iidm-api + ${powsyblcore.version} + + + com.powsybl + powsybl-triple-store-api + ${powsyblcore.version} + + + + com.powsybl + powsybl-iidm-impl + ${powsyblcore.version} + test + + + junit + junit + ${junit.version} + test + + + org.mockito + mockito-all + ${mockito.version} + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + + diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 000000000..62c01f546 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..1bd55f5f7 --- /dev/null +++ b/pom.xml @@ -0,0 +1,554 @@ + + + + 4.0.0 + + com.powsybl + powsybl-single-line-diagram + 1.0.0-SNAPSHOT + pom + + powsybl single line diagram + PowSyBl single line diagram components + http://www.powsybl.org + + + + Mozilla Public License, Version 2.0 + https://www.mozilla.org/en-US/MPL/2.0/ + + + + + scm:git:https://github.com/powsybl/powsybl-single-line-diagram.git + scm:git:https://github.com/powsybl/powsybl-single-line-diagram.git + https://github.com/powsybl/powsybl-single-line-diagram + + + + + Geoffroy JAMGOTCHIAN + geoffroy.jamgotchian@rte-france.com + RTE + http://www.rte-france.com + + + Benoit JEANSON + benoit.jeanson@rte-france.com + RTE + http://www.rte-france.com + + + + + base-voltage-color + cgmes-dl + single-line-diagram-core + single-line-diagram-cgmes + single-line-diagram-iidm-extensions + single-line-diagram-util + single-line-diagram-view + single-line-diagram-view-app + + + + UTF-8 + + 1.8 + + 3.11.0 + 1.9 + 1.3.1 + 0.1 + 1.1 + 1.0.1 + 4.12 + 1.2.3 + 27.0.1-jre + 1.10.19 + 2.4.4 + 1.7.22 + 2.3.0 + + 1.8 + 3.1.0 + 3.0.0 + 1.4 + 3.0.0 + 3.1.0 + 3.8.0 + 3.3.9 + 3.1.1 + 2.8.2 + 3.0.0-M2 + 3.0.5 + 1.6 + 2.5.2 + 0.8.1 + 3.1.0 + 3.0.1 + 3.5.2 + 3.1.0 + 3.1.1 + 3.7.1 + 3.0.1 + 2.22.0 + 1.0.0 + 2.7 + + + **/generated/**/* + + + 3.0.0-SNAPSHOT + + + + + + org.codehaus.mojo + findbugs-maven-plugin + ${maven.findbugs.version} + + true + true + + + + org.codehaus.mojo + versions-maven-plugin + ${maven.versions.version} + + false + + + + + + + org.apache.maven.plugins + maven-clean-plugin + ${maven.clean.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.version} + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-install-plugin + ${maven.install.version} + + + org.apache.maven.plugins + maven-site-plugin + ${maven.site.version} + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.version} + + + org.apache.maven.plugins + maven-resources-plugin + ${maven.resources.version} + + + org.apache.maven.plugins + maven-assembly-plugin + ${maven.assembly.version} + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven.dependency.version} + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven.deploy.version} + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.version} + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.version} + + + org.codehaus.mojo + buildnumber-maven-plugin + ${maven.buildnumber.version} + + + validate + + create + + + + + false + false + UNKNOWN_BUILD + + + + org.codehaus.mojo + templating-maven-plugin + ${maven.templating.version} + + + filter-src + + filter-sources + + + + + + + + + + + jacoco + + false + + + + + org.jacoco + jacoco-maven-plugin + ${maven.jacoco.version} + + + + **/generated/**/* + + + + + prepare-agent + + prepare-agent + + + + + + + + + checks + + true + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven.checkstyle.version} + + + validate + + check + + + checkstyle.xml + true + true + **/generated/**/* + true + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven.enforcer.version} + + + validate + + enforce + + + false + + + + ${maven.core.version} + + + + + + + + + + + release + + false + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven.gpg.version} + + + verify + + sign + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.version} + + + javadoc-jar + package + + jar + + + + javadoc-aggregate-jar + package + false + + aggregate-jar + + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven.source.version} + + + package + + jar-no-fork + test-jar-no-fork + + + + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + + + + + commons-cli + commons-cli + ${commonscli.version} + + + com.github.afester.javafx + FranzXaver + ${franzxaver.version} + + + com.google.guava + guava + ${guava.version} + + + org.apache.xmlgraphics + batik-anim + ${batik.version} + + + org.apache.xmlgraphics + batik-bridge + ${batik.version} + + + org.jgrapht + jgrapht-core + ${jgrapht.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + + com.powsybl + powsybl-iidm-api + ${powsyblcore.version} + + + com.powsybl + powsybl-iidm-converter-api + ${powsyblcore.version} + + + com.powsybl + powsybl-cgmes-dl-conversion + ${project.version} + + + com.powsybl + powsybl-cgmes-iidm-extensions + ${project.version} + + + + + ch.qos.logback + logback-classic + ${logback.version} + runtime + + + org.eclipse.rdf4j + rdf4j-rio-rdfxml + ${rdf4j.version} + runtime + + + org.eclipse.rdf4j + rdf4j-runtime + ${rdf4j.version} + runtime + + + com.powsybl + powsybl-iidm-impl + ${powsyblcore.version} + runtime + + + + com.powsybl + powsybl-cgmes-conversion + ${powsyblcore.version} + runtime + + + com.powsybl + powsybl-config-classic + ${powsyblcore.version} + runtime + + + com.powsybl + powsybl-iidm-xml-converter + ${powsyblcore.version} + runtime + + + com.powsybl + powsybl-triple-store-impl-rdf4j + ${powsyblcore.version} + runtime + + + + + com.google.guava + guava-testlib + ${guava.version} + test + + + com.google.jimfs + jimfs + ${jimfs.version} + test + + + junit + junit + ${junit.version} + test + + + org.assertj + assertj-core + ${assertj.version} + test + + + org.mockito + mockito-all + ${mockito.version} + test + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + test + + + org.slf4j + slf4j-simple + ${slf4j.version} + test + + + org.xmlunit + xmlunit-core + ${xmlunit.version} + test + + + + com.powsybl + powsybl-iidm-test + ${powsyblcore.version} + test + + + + + diff --git a/single-line-diagram-cgmes/pom.xml b/single-line-diagram-cgmes/pom.xml new file mode 100644 index 000000000..57e92f579 --- /dev/null +++ b/single-line-diagram-cgmes/pom.xml @@ -0,0 +1,68 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-single-line-diagram + 1.0.0-SNAPSHOT + + + powsybl-single-line-diagram-cgmes + Single line diagram based on CGMES-DL + + + + com.powsybl + powsybl-cgmes-dl-conversion + + + com.powsybl + powsybl-cgmes-iidm-extensions + + + com.powsybl + powsybl-iidm-api + + + + com.powsybl + powsybl-single-line-diagram-core + ${project.version} + + + + + com.powsybl + powsybl-cgmes-iidm-extensions + ${project.version} + test-jar + test + + + com.powsybl + powsybl-iidm-impl + test + + + junit + junit + test + + + org.slf4j + slf4j-simple + test + + + diff --git a/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/AbstractCgmesLayout.java b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/AbstractCgmesLayout.java new file mode 100644 index 000000000..758e0714f --- /dev/null +++ b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/AbstractCgmesLayout.java @@ -0,0 +1,344 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import java.util.Arrays; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.CouplingDeviceDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.ThreeWindingsTransformerDiagramData; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.iidm.network.StaticVarCompensator; +import com.powsybl.iidm.network.Switch; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +import static com.powsybl.sld.library.ComponentTypeName.CAPACITOR; +import static com.powsybl.sld.library.ComponentTypeName.GENERATOR; +import static com.powsybl.sld.library.ComponentTypeName.INDUCTOR; +import static com.powsybl.sld.library.ComponentTypeName.LINE; +import static com.powsybl.sld.library.ComponentTypeName.DANGLING_LINE; +import static com.powsybl.sld.library.ComponentTypeName.LOAD; +import static com.powsybl.sld.library.ComponentTypeName.PHASE_SHIFT_TRANSFORMER; +import static com.powsybl.sld.library.ComponentTypeName.STATIC_VAR_COMPENSATOR; +import static com.powsybl.sld.library.ComponentTypeName.THREE_WINDINGS_TRANSFORMER; +import static com.powsybl.sld.library.ComponentTypeName.TWO_WINDINGS_TRANSFORMER; +import static com.powsybl.sld.library.ComponentTypeName.VSC_CONVERTER_STATION; +import com.powsybl.sld.model.BusNode; +import com.powsybl.sld.model.FeederNode; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.Node; +import com.powsybl.sld.model.SwitchNode; +import com.powsybl.sld.model.Node.NodeType; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractCgmesLayout { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractCgmesLayout.class); + protected static final double X_MARGIN = 20; + protected static final double Y_MARGIN = 10; + protected static final double LINE_OFFSET = 20; + + protected double minX = 0; + protected double minY = 0; + protected boolean rotatedBus = false; + protected boolean isNodeBreaker = true; + protected boolean fixTransformersLabel = false; + + protected void setMin(double x, double y) { + if (minX == 0 || x < minX) { + minX = x; + } + if (minY == 0 || y < minY) { + minY = y; + } + } + + protected Graph removeFictitiousNodes(Graph graph) { + graph.removeUnnecessaryFictitiousNodes(); + graph.removeFictitiousSwitchNodes(); + return graph; + } + + protected void setNodeCoordinates(Graph graph, String diagramName) { + isNodeBreaker = TopologyKind.NODE_BREAKER.equals(graph.getVoltageLevel().getTopologyKind()); + // skip line nodes: I need the coordinates of the adjacent node to know which side of the line belongs to this voltage level + graph.getNodes().stream().filter(node -> !isLineNode(node)).forEach(node -> setNodeCoordinates(graph, node, diagramName)); + // set line nodes coordinates: I use the coordinates of the adjacent node to know which side of the line belongs to this voltage level + graph.getNodes().stream().filter(this::isLineNode).forEach(node -> setLineNodeCoordinates(graph, node, diagramName)); + } + + protected boolean isLineNode(Node node) { + return Arrays.asList(LINE, DANGLING_LINE, VSC_CONVERTER_STATION).contains(node.getComponentType()); + } + + protected void setNodeCoordinates(Graph graph, Node node, String diagramName) { + LOG.info("Setting coordinates of node {}, type {}, component type {}", node.getId(), node.getType(), node.getComponentType()); + switch (node.getType()) { + case BUS: + BusNode busNode = (BusNode) node; + if (TopologyKind.NODE_BREAKER.equals(graph.getVoltageLevel().getTopologyKind())) { + BusbarSection busbar = graph.getVoltageLevel().getConnectable(busNode.getId(), BusbarSection.class); + NodeDiagramData busbarDiagramData = busbar != null ? busbar.getExtension(NodeDiagramData.class) : null; + setBusNodeCoordinates(busNode, busbarDiagramData, diagramName); + } else { + Bus bus = graph.getVoltageLevel().getBusBreakerView().getBus(busNode.getId()); + NodeDiagramData busDiagramData = bus != null ? bus.getExtension(NodeDiagramData.class) : null; + setBusNodeCoordinates(busNode, busDiagramData, diagramName); + } + break; + case SWITCH: + SwitchNode switchNode = (SwitchNode) node; + Switch sw = TopologyKind.NODE_BREAKER.equals(graph.getVoltageLevel().getTopologyKind()) ? + graph.getVoltageLevel().getNodeBreakerView().getSwitch(switchNode.getId()) : + graph.getVoltageLevel().getBusBreakerView().getSwitch(switchNode.getId()); + CouplingDeviceDiagramData switchDiagramData = sw != null ? sw.getExtension(CouplingDeviceDiagramData.class) : null; + setCouplingDeviceNodeCoordinates(switchNode, switchDiagramData, true, diagramName); + break; + case FEEDER: + setFeederNodeCoordinates(graph, node, diagramName); + break; + default: + break; + } + } + + protected void setBusNodeCoordinates(BusNode node, NodeDiagramData diagramData, String diagramName) { + if (diagramData != null) { + NodeDiagramData.NodeDiagramDataDetails diagramDetails = diagramData.getData(diagramName); + if (diagramDetails != null) { + node.setX(diagramDetails.getPoint1().getX()); + node.setY(diagramDetails.getPoint1().getY()); + node.setPxWidth(computeBusWidth(diagramDetails)); + rotatedBus = diagramDetails.getPoint1().getX() == diagramDetails.getPoint2().getX(); + node.setRotationAngle(rotatedBus ? 90. : null); + setMin(diagramDetails.getPoint1().getX(), diagramDetails.getPoint1().getY()); + } else { + LOG.warn("No CGMES-DL data for {} node {}, bus {}, diagramName {}", node.getType(), node.getId(), node.getName(), diagramName); + } + } else { + LOG.warn("No CGMES-DL data for {} node {}, bus {}", node.getType(), node.getId(), node.getName()); + } + } + + protected void setCouplingDeviceNodeCoordinates(Node node, CouplingDeviceDiagramData diagramData, boolean rotate, String diagramName) { + if (diagramData != null) { + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails diagramDetails = diagramData.getData(diagramName); + if (diagramDetails != null) { + node.setX(diagramDetails.getPoint().getX()); + node.setY(diagramDetails.getPoint().getY()); + node.setRotationAngle(rotate && (diagramDetails.getRotation() == 90 || diagramDetails.getRotation() == 270) ? diagramDetails.getRotation() : null); + setMin(diagramDetails.getPoint().getX(), diagramDetails.getPoint().getY()); + } else { + LOG.warn("No CGMES-DL data for {} node {}, name {}, diagramName {}", node.getType(), node.getId(), node.getName(), diagramName); + } + + } else { + LOG.warn("No CGMES-DL data for {} node {}, name {}", node.getType(), node.getId(), node.getName()); + } + } + + protected double computeBusWidth(NodeDiagramData.NodeDiagramDataDetails diagramData) { + if (diagramData.getPoint1().getX() == diagramData.getPoint2().getX()) { + return Math.abs(diagramData.getPoint1().getY() - diagramData.getPoint2().getY()); + } else { + return Math.abs(diagramData.getPoint1().getX() - diagramData.getPoint2().getX()); + } + } + + protected void setFeederNodeCoordinates(Graph graph, Node node, String diagramName) { + switch (node.getComponentType()) { + case LOAD: + FeederNode loadNode = (FeederNode) node; + Load load = graph.getVoltageLevel().getConnectable(loadNode.getId(), Load.class); + InjectionDiagramData loadDiagramData = load != null ? load.getExtension(InjectionDiagramData.class) : null; + setInjectionNodeCoordinates(loadNode, loadDiagramData, true, diagramName); + break; + case GENERATOR: + FeederNode generatorNode = (FeederNode) node; + Generator generator = graph.getVoltageLevel().getConnectable(generatorNode.getId(), Generator.class); + InjectionDiagramData generatorDiagramData = generator != null ? generator.getExtension(InjectionDiagramData.class) : null; + setInjectionNodeCoordinates(generatorNode, generatorDiagramData, false, diagramName); + break; + case CAPACITOR: + case INDUCTOR: + FeederNode shuntNode = (FeederNode) node; + ShuntCompensator shunt = graph.getVoltageLevel().getConnectable(shuntNode.getId(), ShuntCompensator.class); + InjectionDiagramData shuntDiagramData = shunt != null ? shunt.getExtension(InjectionDiagramData.class) : null; + setInjectionNodeCoordinates(shuntNode, shuntDiagramData, true, diagramName); + break; + case STATIC_VAR_COMPENSATOR: + FeederNode svcNode = (FeederNode) node; + StaticVarCompensator svc = graph.getVoltageLevel().getConnectable(svcNode.getId(), StaticVarCompensator.class); + InjectionDiagramData svcDiagramData = svc != null ? svc.getExtension(InjectionDiagramData.class) : null; + setInjectionNodeCoordinates(svcNode, svcDiagramData, true, diagramName); + break; + case TWO_WINDINGS_TRANSFORMER: + case PHASE_SHIFT_TRANSFORMER: + FeederNode transformerNode = (FeederNode) node; + TwoWindingsTransformer transformer = graph.getVoltageLevel().getConnectable(getBranchId(transformerNode.getId()), TwoWindingsTransformer.class); + CouplingDeviceDiagramData transformerDiagramData = null; + if (transformer != null) { + transformerDiagramData = transformer.getExtension(CouplingDeviceDiagramData.class); + setTransformersLabel(transformerNode, graph.isUseName(), transformer.getName(), transformer.getId()); + } + setCouplingDeviceNodeCoordinates(transformerNode, transformerDiagramData, false, diagramName); + break; + case THREE_WINDINGS_TRANSFORMER: + FeederNode transformer3wNode = (FeederNode) node; + ThreeWindingsTransformer transformer3w = graph.getVoltageLevel().getConnectable(getBranchId(transformer3wNode.getId()), ThreeWindingsTransformer.class); + ThreeWindingsTransformerDiagramData transformer3wDiagramData = null; + if (transformer3w != null) { + transformer3wDiagramData = transformer3w.getExtension(ThreeWindingsTransformerDiagramData.class); + setTransformersLabel(transformer3wNode, graph.isUseName(), transformer3w.getName(), transformer3w.getId()); + } + setThreeWindingsTransformerNodeCoordinates(transformer3wNode, transformer3wDiagramData, diagramName); + break; + default: + break; + } + } + + protected void setTransformersLabel(FeederNode node, boolean useNames, String name, String id) { + if (fixTransformersLabel) { + String label = useNames ? name : id; + node.setLabel(label); + } + } + + protected String getBranchId(String branchNodeId) { + return branchNodeId.substring(0, branchNodeId.lastIndexOf('_')); + } + + protected void setInjectionNodeCoordinates(FeederNode node, InjectionDiagramData diagramData, boolean rotate, String diagramName) { + if (diagramData != null) { + InjectionDiagramData.InjectionDiagramDetails diagramDetails = diagramData.getData(diagramName); + if (diagramDetails != null) { + node.setX(diagramDetails.getPoint().getX()); + node.setY(diagramDetails.getPoint().getY()); + node.setRotationAngle(rotate && (diagramDetails.getRotation() == 90 || diagramDetails.getRotation() == 270) ? diagramDetails.getRotation() : null); + setMin(diagramDetails.getPoint().getX(), diagramDetails.getPoint().getY()); + } else { + LOG.warn("No CGMES-DL data for {} {} node {}, injection {}, diagramName {}", node.getType(), node.getComponentType(), node.getId(), node.getName(), diagramName); + } + } else { + LOG.warn("No CGMES-DL data for {} {} node {}, injection {}", node.getType(), node.getComponentType(), node.getId(), node.getName()); + } + } + + protected void setThreeWindingsTransformerNodeCoordinates(FeederNode node, ThreeWindingsTransformerDiagramData diagramData, String diagramName) { + if (diagramData != null) { + ThreeWindingsTransformerDiagramData.ThreeWindingsTransformerDiagramDataDetails diagramDetails = diagramData.getData(diagramName); + if (diagramDetails != null) { + node.setX(diagramDetails.getPoint().getX()); + node.setY(diagramDetails.getPoint().getY()); + setMin(diagramDetails.getPoint().getX(), diagramDetails.getPoint().getY()); + } else { + LOG.warn("No CGMES-DL data for {} {} node {}, transformer {}, diagramName {}", node.getType(), node.getComponentType(), node.getId(), node.getName(), diagramName); + } + } else { + LOG.warn("No CGMES-DL data for {} {} node {}, transformer {}", node.getType(), node.getComponentType(), node.getId(), node.getName()); + } + } + + protected void setLineNodeCoordinates(Graph graph, Node node, String diagramName) { + LOG.info("Setting coordinates of node {}, type {}, component type {}", node.getId(), node.getType(), node.getComponentType()); + switch (node.getComponentType()) { + case LINE: + FeederNode lineNode = (FeederNode) node; + Line line = graph.getVoltageLevel().getConnectable(getBranchId(lineNode.getId()), Line.class); + LineDiagramData lineDiagramData = line != null ? line.getExtension(LineDiagramData.class) : null; + setLineNodeCoordinates(lineNode, lineDiagramData, diagramName); + break; + case DANGLING_LINE: + FeederNode danglingLineNode = (FeederNode) node; + DanglingLine danglingLine = graph.getVoltageLevel().getConnectable(danglingLineNode.getId(), DanglingLine.class); + LineDiagramData danglingLineDiagramData = danglingLine != null ? danglingLine.getExtension(LineDiagramData.class) : null; + setLineNodeCoordinates(danglingLineNode, danglingLineDiagramData, diagramName); + break; + default: + break; + } + } + + protected void setLineNodeCoordinates(FeederNode node, LineDiagramData diagramData, String diagramName) { + if (diagramData != null) { + if (diagramData.getDiagramsNames().contains(diagramName)) { + DiagramPoint linePoint = getLinePoint(diagramData, node, diagramName); + node.setX(linePoint.getX()); + node.setY(linePoint.getY()); + node.setRotationAngle(rotatedBus ? 90. : null); + setMin(linePoint.getX(), linePoint.getY()); + } else { + LOG.warn("No CGMES-DL data for {} {} node {}, line {}, diagramName {}", node.getType(), node.getComponentType(), node.getId(), node.getName(), diagramName); + } + } else { + LOG.warn("No CGMES-DL data for {} {} node {}, line {}", node.getType(), node.getComponentType(), node.getId(), node.getName()); + } + } + + protected DiagramPoint getLinePoint(LineDiagramData lineDiagramData, Node lineNode, String diagramName) { + DiagramPoint adjacentNodePoint = getLineAdjacentNodePoint(lineNode); + if (adjacentNodePoint == null) { + return getLinePoint(lineDiagramData, true, diagramName); + } + double firstPointDistance = Math.hypot(lineDiagramData.getFirstPoint(diagramName).getX() - adjacentNodePoint.getX(), + lineDiagramData.getFirstPoint(diagramName).getY() - adjacentNodePoint.getY()); + double lastPointDistance = Math.hypot(lineDiagramData.getLastPoint(diagramName).getX() - adjacentNodePoint.getX(), + lineDiagramData.getLastPoint(diagramName).getY() - adjacentNodePoint.getY()); + return getLinePoint(lineDiagramData, firstPointDistance > lastPointDistance, diagramName); + } + + protected DiagramPoint getLineAdjacentNodePoint(Node branchNode) { + List adjacentNodes = branchNode.getAdjacentNodes(); + if (adjacentNodes == null || adjacentNodes.isEmpty()) { + return null; + } + Node adjacentNode = adjacentNodes.get(0); // as we are working on a single voltage level a line node should be connected to only 1 node + // a line should not be connected to another line, so I should already have the coordinates of the adjacent node + return new DiagramPoint(adjacentNode.getX(), adjacentNode.getY(), 0); + } + + protected DiagramPoint getLinePoint(LineDiagramData lineDiagramData, boolean isLastPointCloser, String diagramName) { + if (isNodeBreaker) { + return isLastPointCloser ? lineDiagramData.getLastPoint(diagramName) : lineDiagramData.getFirstPoint(diagramName); + } + return isLastPointCloser ? lineDiagramData.getLastPoint(diagramName, LINE_OFFSET) : lineDiagramData.getFirstPoint(diagramName, LINE_OFFSET); + } + + protected void shiftNodeCoordinates(Node node, double scaleFactor) { + node.setX(node.getX() - minX + (X_MARGIN / scaleFactor)); + node.setY(node.getY() - minY + (Y_MARGIN / scaleFactor)); + } + + protected void scaleNodeCoordinates(Node node, double scaleFactor) { + node.setX(node.getX() * scaleFactor); + node.setY(node.getY() * scaleFactor); + if (node.getType() == NodeType.BUS) { + BusNode nodeBus = (BusNode) node; + nodeBus.setPxWidth(nodeBus.getPxWidth() * scaleFactor); + } + } + +} diff --git a/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesSubstationLayout.java b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesSubstationLayout.java new file mode 100644 index 000000000..91f30f1ce --- /dev/null +++ b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesSubstationLayout.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import com.powsybl.iidm.network.Network; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.SubstationLayout; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.SubstationGraph; + +/** + * + * @author Massimo Ferraro + */ +public class CgmesSubstationLayout extends AbstractCgmesLayout implements SubstationLayout { + + private static final Logger LOG = LoggerFactory.getLogger(CgmesSubstationLayout.class); + + private final SubstationGraph graph; + + public CgmesSubstationLayout(SubstationGraph graph) { + Objects.requireNonNull(graph); + for (Graph vlGraph : graph.getNodes()) { + removeFictitiousNodes(vlGraph); + } + fixTransformersLabel = true; + this.graph = graph; + } + + @Override + public void run(LayoutParameters layoutParam) { + String diagramName = layoutParam.getDiagramName(); + if (diagramName == null) { + LOG.warn("layout parameter diagramName not set: CGMES-DL layout will not be applied"); + return; + } + Network network = graph.getSubstation().getNetwork(); + if (!NetworkDiagramData.containsDiagramName(network, diagramName)) { + LOG.warn("diagram name {} not found in network: CGMES-DL layout will not be applied to network {}, substation {}", diagramName, network.getId(), graph.getSubstation().getId()); + return; + } + LOG.info("Applying CGMES-DL layout to network {}, substation {}, diagram name {}", network.getId(), graph.getSubstation().getId(), diagramName); + for (Graph vlGraph : graph.getNodes()) { + setNodeCoordinates(vlGraph, diagramName); + } + for (Graph vlGraph : graph.getNodes()) { + vlGraph.getNodes().forEach(node -> shiftNodeCoordinates(node, layoutParam.getScaleFactor())); + } + if (layoutParam.getScaleFactor() != 1) { + for (Graph vlGraph : graph.getNodes()) { + vlGraph.getNodes().forEach(node -> scaleNodeCoordinates(node, layoutParam.getScaleFactor())); + } + } + } + +} diff --git a/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesSubstationLayoutFactory.java b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesSubstationLayoutFactory.java new file mode 100644 index 000000000..aa8bb2dc6 --- /dev/null +++ b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesSubstationLayoutFactory.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import com.powsybl.sld.layout.SubstationLayout; +import com.powsybl.sld.layout.SubstationLayoutFactory; +import com.powsybl.sld.layout.VoltageLevelLayoutFactory; +import com.powsybl.sld.model.SubstationGraph; + +/** + * + * @author Massimo Ferraro + */ +public class CgmesSubstationLayoutFactory implements SubstationLayoutFactory { + + @Override + public SubstationLayout create(SubstationGraph graph, VoltageLevelLayoutFactory vLayoutFactory) { + return new CgmesSubstationLayout(graph); + } + +} diff --git a/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesVoltageLevelLayout.java b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesVoltageLevelLayout.java new file mode 100644 index 000000000..fb1d53f77 --- /dev/null +++ b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesVoltageLevelLayout.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import com.powsybl.iidm.network.Network; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.VoltageLevelLayout; +import com.powsybl.sld.model.Graph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; + +/** + * + * @author Massimo Ferraro + */ +public class CgmesVoltageLevelLayout extends AbstractCgmesLayout implements VoltageLevelLayout { + + private static final Logger LOG = LoggerFactory.getLogger(CgmesVoltageLevelLayout.class); + + private final Graph graph; + + public CgmesVoltageLevelLayout(Graph graph) { + Objects.requireNonNull(graph); + this.graph = removeFictitiousNodes(graph); + } + + @Override + public void run(LayoutParameters layoutParam) { + String diagramName = layoutParam.getDiagramName(); + if (diagramName == null) { + LOG.warn("layout parameter diagramName not set: CGMES-DL layout will not be applied"); + } else { + Network network = graph.getVoltageLevel().getSubstation().getNetwork(); + if (NetworkDiagramData.containsDiagramName(network, diagramName)) { + LOG.info("Applying CGMES-DL layout to network {}, voltage level {}, diagram name {}", network.getId(), graph.getVoltageLevel().getId(), diagramName); + + setNodeCoordinates(graph, diagramName); + graph.getNodes().forEach(node -> shiftNodeCoordinates(node, layoutParam.getScaleFactor())); + if (layoutParam.getScaleFactor() != 1) { + graph.getNodes().forEach(node -> scaleNodeCoordinates(node, layoutParam.getScaleFactor())); + } + } else { + LOG.warn("diagram name {} not found in network: CGMES-DL layout will not be applied to network {}, voltage level {}", diagramName, network.getId(), graph.getVoltageLevel().getId()); + } + } + } +} diff --git a/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesVoltageLevelLayoutFactory.java b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesVoltageLevelLayoutFactory.java new file mode 100644 index 000000000..cfb68fc31 --- /dev/null +++ b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/CgmesVoltageLevelLayoutFactory.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import com.powsybl.sld.layout.VoltageLevelLayout; +import com.powsybl.sld.layout.VoltageLevelLayoutFactory; +import com.powsybl.sld.model.Graph; + +/** + * + * @author Massimo Ferraro + */ +public class CgmesVoltageLevelLayoutFactory implements VoltageLevelLayoutFactory { + + @Override + public VoltageLevelLayout create(Graph graph) { + return new CgmesVoltageLevelLayout(graph); + } + +} diff --git a/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/LayoutToCgmesDlExporterTool.java b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/LayoutToCgmesDlExporterTool.java new file mode 100644 index 000000000..8cbe0efb6 --- /dev/null +++ b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/LayoutToCgmesDlExporterTool.java @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import com.google.auto.service.AutoService; +import com.google.common.collect.ImmutableMap; +import com.powsybl.cgmes.dl.conversion.CgmesDLExporter; +import com.powsybl.cgmes.dl.conversion.CgmesDLUtils; +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.datasource.FileDataSource; +import com.powsybl.iidm.import_.Importers; +import com.powsybl.iidm.network.Network; +import com.powsybl.sld.layout.*; +import com.powsybl.tools.Command; +import com.powsybl.tools.Tool; +import com.powsybl.tools.ToolOptions; +import com.powsybl.tools.ToolRunningContext; +import com.powsybl.triplestore.api.TripleStore; +import com.powsybl.triplestore.api.TripleStoreFactory; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +import java.io.UnsupportedEncodingException; +import java.nio.file.Path; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author Christian Biasuzzi + */ +@AutoService(Tool.class) +public class LayoutToCgmesDlExporterTool implements Tool { + + private static final String INPUT_FILE = "input-file"; + private static final String OUTPUT_DIR = "output-dir"; + private static final String VOLTAGE_LEVEL_LAYOUT = "voltage-level-layout"; + private static final String SUBSTATION_LAYOUT = "substation-layout"; + private static final String DEFAULT_VOLTAGE_LAYOUT = "auto-without-extensions"; + private static final String DEFAULT_SUBSTATION_LAYOUT = "horizontal"; + + private final Map voltageLevelsLayouts + = ImmutableMap.of("auto-extensions", new PositionVoltageLevelLayoutFactory(new PositionFromExtension()), + DEFAULT_VOLTAGE_LAYOUT, new PositionVoltageLevelLayoutFactory(new PositionFree())); + + private final Map substationsLayouts + = ImmutableMap.of(DEFAULT_SUBSTATION_LAYOUT, new HorizontalSubstationLayoutFactory(), + "vertical", new VerticalSubstationLayoutFactory()); + + @Override + public Command getCommand() { + return new Command() { + + @Override + public String getName() { + return "generate-cgmes-dl"; + } + + @Override + public String getTheme() { + return "Substation diagram"; + } + + @Override + public String getDescription() { + return "apply a layout to a network, generate and export a new CGMES-DL profile"; + } + + @Override + public Options getOptions() { + Options options = new Options(); + options.addOption(Option.builder().longOpt(INPUT_FILE) + .desc("input file") + .hasArg() + .argName("INPUT_FILE") + .required() + .build()); + options.addOption(Option.builder().longOpt(OUTPUT_DIR) + .desc("output directory") + .hasArg() + .argName("OUTPUT_DIR") + .required() + .build()); + options.addOption(Option.builder().longOpt(VOLTAGE_LEVEL_LAYOUT) + .desc("voltage level layout") + .hasArg() + .argName("VOLTAGE LEVEL LAYOUT") + .build()); + options.addOption(Option.builder().longOpt(SUBSTATION_LAYOUT) + .desc("substation layout") + .hasArg() + .argName("SUBSTATION LAYOUT") + .build()); + return options; + } + + @Override + public String getUsageFooter() { + return "Where SUBSTATION LAYOUT is one of: " + substationsLayouts.keySet().stream().collect(Collectors.joining(", ")) + " (default is: " + DEFAULT_SUBSTATION_LAYOUT + ")" + + " and VOLTAGE LEVEL LAYOUT is one of: " + voltageLevelsLayouts.keySet().stream().collect(Collectors.joining(", ")) + " (default is: " + DEFAULT_VOLTAGE_LAYOUT + ")"; + } + }; + } + + @Override + public void run(CommandLine line, ToolRunningContext context) throws UnsupportedEncodingException { + ToolOptions toolOptions = new ToolOptions(line, context); + Path inputFile = toolOptions.getPath(INPUT_FILE).orElseThrow(() -> new PowsyblException(INPUT_FILE + " parameter is missing")); + Path outputDir = toolOptions.getPath(OUTPUT_DIR).orElseThrow(() -> new PowsyblException(OUTPUT_DIR + " parameter is missing")); + + String substationLayout = toolOptions.getValue(SUBSTATION_LAYOUT).orElse(DEFAULT_SUBSTATION_LAYOUT); + String voltageLayout = toolOptions.getValue(VOLTAGE_LEVEL_LAYOUT).orElse(DEFAULT_VOLTAGE_LAYOUT); + SubstationLayoutFactory sFactory = substationsLayouts.get(substationLayout); + if (sFactory == null) { + throw new PowsyblException("invalid " + SUBSTATION_LAYOUT + ": " + substationLayout); + } + VoltageLevelLayoutFactory vFactory = voltageLevelsLayouts.get(voltageLayout); + if (vFactory == null) { + throw new PowsyblException("invalid " + VOLTAGE_LEVEL_LAYOUT + ": " + voltageLayout); + } + + context.getOutputStream().println("Loading network '" + inputFile + "'..."); + Network network = Importers.loadNetwork(inputFile); + + context.getOutputStream().println("Generating layout for the network ..."); + LayoutToCgmesExtensionsConverter lTranslator = new LayoutToCgmesExtensionsConverter(sFactory, vFactory, new LayoutParameters(), true); + lTranslator.convertLayout(network, null); + + context.getOutputStream().println("Exporting network data (including the DL file) to " + outputDir); + TripleStore tStore = CgmesDLUtils.getTripleStore(network); + if (tStore == null) { + tStore = TripleStoreFactory.create(); + } + CgmesDLExporter dlExporter = new CgmesDLExporter(network, tStore); + dlExporter.exportDLData(new FileDataSource(outputDir, network.getName())); + } +} diff --git a/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/LayoutToCgmesExtensionsConverter.java b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/LayoutToCgmesExtensionsConverter.java new file mode 100644 index 000000000..1d3b9edd5 --- /dev/null +++ b/single-line-diagram-cgmes/src/main/java/com/powsybl/sld/cgmes/LayoutToCgmesExtensionsConverter.java @@ -0,0 +1,369 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import com.powsybl.cgmes.dl.conversion.CgmesDLUtils; +import com.powsybl.cgmes.iidm.extensions.dl.*; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.layout.*; +import com.powsybl.sld.model.*; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.powsybl.sld.library.ComponentTypeName.DANGLING_LINE; +import static com.powsybl.sld.library.ComponentTypeName.LINE; +import static com.powsybl.sld.library.ComponentTypeName.THREE_WINDINGS_TRANSFORMER; +import static com.powsybl.sld.library.ComponentTypeName.TWO_WINDINGS_TRANSFORMER; +import static com.powsybl.sld.library.ComponentTypeName.VSC_CONVERTER_STATION; + +/** + * @author Christian Biasuzzi + */ +public class LayoutToCgmesExtensionsConverter { + + private static final Logger LOG = LoggerFactory.getLogger(LayoutToCgmesExtensionsConverter.class); + + private static final double OFFSET_MULTIPLIER_X = 2.0; + + private final LayoutParameters lparams; + private final SubstationLayoutFactory sFactory; + private final VoltageLevelLayoutFactory vFactory; + private final boolean showNames; + + public LayoutToCgmesExtensionsConverter(SubstationLayoutFactory sFactory, VoltageLevelLayoutFactory vFactory, LayoutParameters lparams, boolean showNames) { + this.sFactory = Objects.requireNonNull(sFactory); + this.vFactory = Objects.requireNonNull(vFactory); + this.lparams = Objects.requireNonNull(lparams); + this.showNames = showNames; + } + + public LayoutToCgmesExtensionsConverter() { + this(new HorizontalSubstationLayoutFactory(), new PositionVoltageLevelLayoutFactory(new PositionFree()), new LayoutParameters(), true); + } + + private boolean isLineNode(Node node) { + return Arrays.asList(LINE, DANGLING_LINE, VSC_CONVERTER_STATION).contains(node.getComponentType()); + } + + private String getBranchId(String branchNodeId) { + return branchNodeId.substring(0, branchNodeId.lastIndexOf('_')); + } + + private int getMaxSeq(List diagramPoints) { + Objects.requireNonNull(diagramPoints); + return diagramPoints.stream().sorted(Comparator.reverseOrder()).findFirst().orElse(new DiagramPoint(0, 0, 0)).getSeq(); + } + + private NodeDiagramData setNodeDiagramPoints(NodeDiagramData diagramData, BusNode busNode, OffsetPoint offsetPoint, String diagramName) { + double x1 = busNode.getX(); + double y1 = busNode.getY(); + double x2 = x1; + double y2 = y1; + double pxWidth = busNode.getPxWidth(); + boolean rotatedBus = busNode.isRotated(); + if (!rotatedBus) { + x2 = x1 + pxWidth; + } else { + y2 = y1 + pxWidth; + } + + NodeDiagramData.NodeDiagramDataDetails diagramDetails = diagramData.new NodeDiagramDataDetails(); + DiagramPoint p1 = offsetPoint.newDiagramPoint(x1, y1, 1); + DiagramPoint p2 = offsetPoint.newDiagramPoint(x2, y2, 2); + diagramDetails.setPoint1(p1); + diagramDetails.setPoint2(p2); + diagramData.addData(diagramName, diagramDetails); + return diagramData; + } + + private LayoutInfo applyLayout(Substation substation, double xoffset, double yoffset, String diagramName) { + OffsetPoint offsetPoint = new OffsetPoint(xoffset, yoffset); + + //apply the specified layout + SubstationGraph sgraph = SubstationGraph.create(substation, showNames); + SubstationLayout sLayout = sFactory.create(sgraph, vFactory); + sLayout.run(lparams); + + LayoutInfo subsBoundary = new LayoutInfo(0.0, 0.0); + substation.getVoltageLevelStream().forEach(voltageLevel -> { + Graph vlGraph = sgraph.getNode(voltageLevel.getId()); + + // remove fictitious nodes&switches (no CGMES DL data available for them) + vlGraph.removeUnnecessaryFictitiousNodes(); + vlGraph.removeFictitiousSwitchNodes(); + + double vlNodeMaxX = vlGraph.getNodes().stream().map(Node::getX).sorted(Collections.reverseOrder()).findFirst().orElse(0.0); + double vlNodeMaxY = vlGraph.getNodes().stream().map(Node::getY).sorted(Collections.reverseOrder()).findFirst().orElse(0.0); + subsBoundary.update(vlNodeMaxX, vlNodeMaxY); + + List componentTypeList = vlGraph.getNodes().stream().map(Node::getComponentType).collect(Collectors.toList()); + LOG.debug("Voltage level id: {} ({}); {} ;component types: {}; max x,y: {}, {}", voltageLevel.getId(), voltageLevel.getName(), voltageLevel.getTopologyKind(), componentTypeList, vlNodeMaxX, vlNodeMaxY); + + //iterate over the voltage level's equipments, and fill the IIDM CGMES DL extensions with the computed layout info + voltageLevel.getLoadStream().filter(load -> vlGraph.getNode(load.getId()) != null).forEach(load -> { + Node node = vlGraph.getNode(load.getId()); + DiagramPoint lDiagramPoint = offsetPoint.newDiagramPoint(node.getX(), node.getY(), 0); + InjectionDiagramData loadIidmDiagramData = new InjectionDiagramData<>(load); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = loadIidmDiagramData.new InjectionDiagramDetails(lDiagramPoint, 0); + loadIidmDiagramData.addData(diagramName, diagramDetails); + LOG.debug("setting CGMES DL IIDM extensions for Load: {}, {}", load.getId(), lDiagramPoint); + load.addExtension(InjectionDiagramData.class, loadIidmDiagramData); + }); + + voltageLevel.getGeneratorStream().filter(generator -> vlGraph.getNode(generator.getId()) != null).forEach(generator -> { + Node node = vlGraph.getNode(generator.getId()); + DiagramPoint gDiagramPoint = offsetPoint.newDiagramPoint(node.getX(), node.getY(), 0); + InjectionDiagramData gIidmDiagramData = new InjectionDiagramData<>(generator); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = gIidmDiagramData.new InjectionDiagramDetails(gDiagramPoint, 0); + gIidmDiagramData.addData(diagramName, diagramDetails); + LOG.debug("setting CGMES DL IIDM extensions for Generator: {}, {}", generator.getId(), gDiagramPoint); + generator.addExtension(InjectionDiagramData.class, gIidmDiagramData); + }); + + voltageLevel.getShuntCompensatorStream().filter(shuntCompensator -> vlGraph.getNode(shuntCompensator.getId()) != null).forEach(shuntCompensator -> { + Node node = vlGraph.getNode(shuntCompensator.getId()); + DiagramPoint scDiagramPoint = offsetPoint.newDiagramPoint(node.getX(), node.getY(), 0); + InjectionDiagramData scDiagramData = new InjectionDiagramData<>(shuntCompensator); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = scDiagramData.new InjectionDiagramDetails(scDiagramPoint, 0); + scDiagramData.addData(diagramName, diagramDetails); + LOG.debug("setting CGMES DL IIDM extensions for ShuntCompensator: {}, {}", shuntCompensator.getId(), scDiagramPoint); + shuntCompensator.addExtension(InjectionDiagramData.class, scDiagramData); + }); + + voltageLevel.getStaticVarCompensatorStream().filter(staticVarCompensator -> vlGraph.getNode(staticVarCompensator.getId()) != null).forEach(staticVarCompensator -> { + Node node = vlGraph.getNode(staticVarCompensator.getId()); + DiagramPoint svcDiagramPoint = offsetPoint.newDiagramPoint(node.getX(), node.getY(), 0); + InjectionDiagramData svcDiagramData = new InjectionDiagramData<>(staticVarCompensator); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = svcDiagramData.new InjectionDiagramDetails(svcDiagramPoint, 0); + svcDiagramData.addData(diagramName, diagramDetails); + LOG.debug("setting CGMES DL IIDM extensions for StaticVarCompensator: {}, {}", staticVarCompensator.getId(), svcDiagramPoint); + staticVarCompensator.addExtension(InjectionDiagramData.class, svcDiagramData); + }); + + substation.getTwoWindingsTransformerStream().forEach(twoWindingsTransformer -> vlGraph.getNodes().stream() + .filter(node -> checkNode(twoWindingsTransformer, node)).findFirst().ifPresent(node -> { + FeederNode transformerNode = (FeederNode) node; + DiagramPoint tDiagramPoint = offsetPoint.newDiagramPoint(transformerNode.getX(), transformerNode.getY(), transformerNode.getOrder()); + CouplingDeviceDiagramData transformerIidmDiagramData = new CouplingDeviceDiagramData<>(twoWindingsTransformer); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails diagramDetails = transformerIidmDiagramData.new CouplingDeviceDiagramDetails(tDiagramPoint, rotationValue(transformerNode)); + transformerIidmDiagramData.addData(diagramName, diagramDetails); + LOG.debug("setting CGMES DL IIDM extensions for TwoWindingTransformer: {}, {}", twoWindingsTransformer.getId(), tDiagramPoint); + twoWindingsTransformer.addExtension(CouplingDeviceDiagramData.class, transformerIidmDiagramData); + }) + ); + + substation.getThreeWindingsTransformerStream().forEach(threeWindingsTransformer -> vlGraph.getNodes().stream() + .filter(node -> checkNode(threeWindingsTransformer, node)).findFirst().ifPresent(node -> { + DiagramPoint tDiagramPoint = offsetPoint.newDiagramPoint(node.getX(), node.getY(), 0); + ThreeWindingsTransformerDiagramData transformerIidmDiagramData = new ThreeWindingsTransformerDiagramData(threeWindingsTransformer); + ThreeWindingsTransformerDiagramData.ThreeWindingsTransformerDiagramDataDetails diagramDetails = transformerIidmDiagramData.new ThreeWindingsTransformerDiagramDataDetails(tDiagramPoint, rotationValue(node)); + transformerIidmDiagramData.addData(diagramName, diagramDetails); + LOG.debug("setting CGMES DL IIDM extensions for ThreeWindingTransformer: {}, {}", threeWindingsTransformer.getId(), tDiagramPoint); + threeWindingsTransformer.addExtension(ThreeWindingsTransformerDiagramData.class, transformerIidmDiagramData); + }) + ); + + vlGraph.getNodes().stream().filter(this::isLineNode).forEach(node -> { + switch (node.getComponentType()) { + case LINE: + FeederNode lineNode = (FeederNode) node; + Line line = vlGraph.getVoltageLevel().getConnectable(getBranchId(lineNode.getId()), Line.class); + if (line != null) { + LineDiagramData lineDiagramData = LineDiagramData.getOrCreateDiagramData(line); + int lineSeq = getMaxSeq(lineDiagramData.getPoints(diagramName)) + 1; + DiagramPoint linePoint = offsetPoint.newDiagramPoint(lineNode.getX(), lineNode.getY(), lineSeq); + lineDiagramData.addPoint(diagramName, linePoint); + + LOG.debug("setting CGMES DL IIDM extensions for Line {} ({}), new point {}", line.getId(), line.getName(), linePoint); + line.addExtension(LineDiagramData.class, lineDiagramData); + } + break; + case DANGLING_LINE: + FeederNode danglingLineNode = (FeederNode) node; + DanglingLine danglingLine = vlGraph.getVoltageLevel().getConnectable(danglingLineNode.getId(), DanglingLine.class); + if (danglingLine != null) { + LineDiagramData danglingLineDiagramData = LineDiagramData.getOrCreateDiagramData(danglingLine); + int danglingLineSeq = getMaxSeq(danglingLineDiagramData.getPoints(diagramName)) + 1; + DiagramPoint danglingLinePoint = offsetPoint.newDiagramPoint(danglingLineNode.getX(), danglingLineNode.getY(), danglingLineSeq); + danglingLineDiagramData.addPoint(diagramName, danglingLinePoint); + + LOG.debug("setting CGMES DL IIDM extensions for Dangling line {} ({}), point {}", danglingLine.getId(), danglingLine.getName(), danglingLinePoint); + danglingLine.addExtension(LineDiagramData.class, danglingLineDiagramData); + } + break; + default: + break; + } + }); + + if (TopologyKind.BUS_BREAKER.equals(voltageLevel.getTopologyKind())) { + voltageLevel.getBusBreakerView().getBusStream().forEach(bus -> + vlGraph.getNodeBuses().stream().filter(busNode -> busNode.getId().equals(bus.getId())).findFirst().ifPresent(busNode -> { + NodeDiagramData busDiagramData = NodeDiagramData.getOrCreateDiagramData(bus); + setNodeDiagramPoints(busDiagramData, busNode, offsetPoint, diagramName); + LOG.debug("setting CGMES DL IIDM extensions for Bus {}, {} - {}", bus.getId(), busDiagramData.getData(diagramName).getPoint1(), busDiagramData.getData(diagramName).getPoint2()); + bus.addExtension(NodeDiagramData.class, busDiagramData); + }) + ); + + } else { + voltageLevel.getNodeBreakerView().getBusbarSectionStream().forEach(busbarSection -> + vlGraph.getNodeBuses().stream().filter(busNode -> busNode.getId().equals(busbarSection.getId())).findFirst().ifPresent(busNode -> { + NodeDiagramData busbarSectionDiagramData = NodeDiagramData.getOrCreateDiagramData(busbarSection); + setNodeDiagramPoints(busbarSectionDiagramData, busNode, offsetPoint, diagramName); + LOG.debug("setting CGMES DL IIDM extensions for BusbarSection {}, {} - {}", busbarSection.getId(), busbarSectionDiagramData.getData(diagramName).getPoint1(), busbarSectionDiagramData.getData(diagramName).getPoint2()); + busbarSection.addExtension(NodeDiagramData.class, busbarSectionDiagramData); + }) + ); + + voltageLevel.getNodeBreakerView().getSwitchStream().filter(Objects::nonNull).forEach(sw -> { + Node swNode = vlGraph.getNode(sw.getId()); + if (checkSwitchNode(swNode)) { + CouplingDeviceDiagramData switchIidmDiagramData = new CouplingDeviceDiagramData<>(sw); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails diagramDetails = switchIidmDiagramData.new CouplingDeviceDiagramDetails(offsetPoint.newDiagramPoint(swNode.getX(), swNode.getY(), 0), switchRotationValue(swNode)); + switchIidmDiagramData.addData(diagramName, diagramDetails); + LOG.debug("setting CGMES DL IIDM extensions for Switch {}, {}", sw.getId(), switchIidmDiagramData); + sw.addExtension(CouplingDeviceDiagramData.class, switchIidmDiagramData); + } + }); + } + }); + + return subsBoundary; + } + + private boolean checkSwitchNode(Node swNode) { + return (swNode != null) && swNode.getType().equals(Node.NodeType.SWITCH); + } + + private boolean checkNode(ThreeWindingsTransformer threeWindingsTransformer, Node node) { + return node.getComponentType().equals(THREE_WINDINGS_TRANSFORMER) && + (((node instanceof Fictitious3WTNode) && ((Fictitious3WTNode) node).getTransformer().getId().equals(threeWindingsTransformer.getId())) + || ((node instanceof Feeder3WTNode) && ((Feeder3WTNode) node).getTransformer().getId().equals(threeWindingsTransformer.getId()))); + } + + private boolean checkNode(TwoWindingsTransformer twoWindingsTransformer, Node node) { + return node.getComponentType().equals(TWO_WINDINGS_TRANSFORMER) && node.getId().startsWith(twoWindingsTransformer.getId()); + } + + private double rotationValue(Node node) { + return node.isRotated() ? 0.0 : 180.0; + } + + private double switchRotationValue(Node node) { + return node.isRotated() ? 90.0 : 0.0; + } + + private void convertLayoutSingleDiagram(Network network, Stream subsStream, String diagramName) { + //creates a single CGMES-DL diagram (named diagramName), where each substation + NetworkDiagramData.addDiagramName(network, diagramName); + final double[] xoffset = {0.0}; + subsStream.forEach(s -> { + LOG.debug("Substation {}({} offset: {})", s.getId(), s.getName(), xoffset[0]); + LayoutInfo li = applyLayout(s, xoffset[0], 0.0, diagramName); + xoffset[0] += OFFSET_MULTIPLIER_X * li.getMaxX(); + }); + } + + private void convertLayoutMultipleDiagrams(Network network, Stream subsStream) { + // creates one CGMES-DL diagram for each substation (where each diagram name is the substation's name) + subsStream.forEach(s -> { + String subDiagramName = StringUtils.isEmpty(s.getName()) ? s.getId() : s.getName(); + NetworkDiagramData.addDiagramName(network, subDiagramName); + LOG.debug("Substation {}", subDiagramName); + applyLayout(s, 0.0, 0.0, subDiagramName); + }); + } + + /** + * Apply the layout to the network, creating one or more CGMES-DL diagrams. + * Note that a CGMES-DL diagram refers to a global coordinate system and can include all the network equipments, + * whereas layouts are currently created per-substation (or per-voltage), using a coordinate system that is local to + * the specific substation/voltage. + * + * This method creates either a single CGMES-DL diagram (where each substation is placed on a single row, one next to the other), + * or multiple CGMES_DL diagrams, one per substation. + * + * @param network + * @param diagramName the diagram's name, if null it creates one CGMES-DL diagram for each substation + * (where each diagram name is the substation's name). Otherwise it creates a single CGMES-DL diagram + * (named diagramName). + */ + public void convertLayout(Network network, String diagramName) { + Objects.requireNonNull(network); + LOG.info("Converting layout {} to IIDM CGMES DL extensions for network: {}", sFactory.getClass(), network.getId()); + + //Network could have already defined a set of iidm cgmes extensions, as loaded via the cgmes importer/cgmesDLImport postprocessor. + //Also associated to the network, we have the triplestore with the DL related triples + //clear the CGMES DL profile data from the network's CGMES tiplestore, if it already exists + //and remove any exising IIDM CGMES equipments' extensions + CgmesDLUtils.clearCgmesDl(network); + CgmesDLUtils.removeIidmCgmesExtensions(network); + + if (diagramName != null) { + convertLayoutSingleDiagram(network, network.getSubstationStream(), diagramName); + } else { + convertLayoutMultipleDiagrams(network, network.getSubstationStream()); + } + } + + /** + * Apply the layout to the network, creating one CGMES-DL diagrams per substation. + * + * @param network + */ + + public void convertLayout(Network network) { + convertLayout(network, null); + } + + class LayoutInfo { + double maxX; + double maxY; + + LayoutInfo(double maxNodeX, double maxNodeY) { + this.maxX = maxNodeX; + this.maxY = maxNodeY; + } + + double getMaxX() { + return maxX; + } + + double getMaxY() { + return maxY; + } + + void update(double maxX, double maxY) { + if (maxX > this.maxX) { + this.maxX = maxX; + } + if (maxY > this.maxY) { + this.maxY = maxY; + } + } + } + + class OffsetPoint { + private final double dx; + private final double dy; + + OffsetPoint(double dx, double dy) { + this.dx = dx; + this.dy = dy; + } + + DiagramPoint newDiagramPoint(double x, double y, int seq) { + return new DiagramPoint(x + dx, y + dy, seq); + } + } +} diff --git a/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/AbstractCgmesVoltageLevelLayoutTest.java b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/AbstractCgmesVoltageLevelLayoutTest.java new file mode 100644 index 000000000..d6bcf26c5 --- /dev/null +++ b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/AbstractCgmesVoltageLevelLayoutTest.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.Node; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractCgmesVoltageLevelLayoutTest { + + protected static final String DIAGRAM_NAME = "default"; + + protected void test(VoltageLevel vl) { + Graph graph = Graph.create(vl); + LayoutParameters layoutParameters = new LayoutParameters(); + layoutParameters.setScaleFactor(2); + layoutParameters.setDiagramName(DIAGRAM_NAME); + new CgmesVoltageLevelLayout(graph).run(layoutParameters); + checkGraph(graph); + checkCoordinates(graph); + } + + protected abstract void checkGraph(Graph graph); + + protected void checkAdjacentNodes(Node node, List expectedAdjacentNodes) { + node.getAdjacentNodes().forEach(adjacentNode -> { + assertTrue(expectedAdjacentNodes.contains(adjacentNode.getId())); + }); + } + + protected abstract void checkCoordinates(Graph graph); + +} diff --git a/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/AbstractNodeTopologyTest.java b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/AbstractNodeTopologyTest.java new file mode 100644 index 000000000..8ed5286a8 --- /dev/null +++ b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/AbstractNodeTopologyTest.java @@ -0,0 +1,261 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import com.powsybl.cgmes.iidm.extensions.dl.*; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.Node; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; + +import static com.powsybl.sld.library.ComponentTypeName.BREAKER; +import static com.powsybl.sld.library.ComponentTypeName.BUSBAR_SECTION; +import static com.powsybl.sld.library.ComponentTypeName.DISCONNECTOR; +import static com.powsybl.sld.library.ComponentTypeName.GENERATOR; +import static com.powsybl.sld.library.ComponentTypeName.LINE; +import static org.junit.Assert.assertEquals; + +/** + * + * @author Massimo Ferraro + */ +public abstract class AbstractNodeTopologyTest extends AbstractCgmesVoltageLevelLayoutTest { + + protected VoltageLevel voltageLevel; + protected VoltageLevel voltageLevelWithInternalConnections; + + @Before + public void setUp() { + createNetwork(); + createNetworkWithInternalConnections(); + } + + protected void createNetwork() { + Network network = Network.create("testCase1", "test"); + voltageLevel = createFirstVoltageLevel(network, 0, 4, 0, 1, 1, 2, 2, 3); + voltageLevel.getNodeBreakerView().newInternalConnection() + .setNode1(4) + .setNode2(0) + .add(); + createSecondVoltageLevel(network); + createLine(network, 3); + addDiagramData(network); + NetworkDiagramData.addDiagramName(network, DIAGRAM_NAME); + } + + protected void createNetworkWithInternalConnections() { + Network network = Network.create("testCase1", "test"); + voltageLevelWithInternalConnections = createFirstVoltageLevel(network, 4, 12, 7, 8, 5, 6, 9, 10); + voltageLevelWithInternalConnections.getNodeBreakerView().newInternalConnection() + .setNode1(4) + .setNode2(0) + .add(); + voltageLevelWithInternalConnections.getNodeBreakerView().newInternalConnection() + .setNode1(5) + .setNode2(2) + .add(); + voltageLevelWithInternalConnections.getNodeBreakerView().newInternalConnection() + .setNode1(6) + .setNode2(1) + .add(); + voltageLevelWithInternalConnections.getNodeBreakerView().newInternalConnection() + .setNode1(7) + .setNode2(1) + .add(); + voltageLevelWithInternalConnections.getNodeBreakerView().newInternalConnection() + .setNode1(8) + .setNode2(0) + .add(); + voltageLevelWithInternalConnections.getNodeBreakerView().newInternalConnection() + .setNode1(9) + .setNode2(3) + .add(); + voltageLevelWithInternalConnections.getNodeBreakerView().newInternalConnection() + .setNode1(10) + .setNode2(2) + .add(); + voltageLevelWithInternalConnections.getNodeBreakerView().newInternalConnection() + .setNode1(11) + .setNode2(3) + .add(); + voltageLevelWithInternalConnections.getNodeBreakerView().newInternalConnection() + .setNode1(12) + .setNode2(0) + .add(); + createSecondVoltageLevel(network); + createLine(network, 11); + addDiagramData(network); + NetworkDiagramData.addDiagramName(network, DIAGRAM_NAME); + } + + protected VoltageLevel createFirstVoltageLevel(Network network, int busbarNode, int generatorNode, int disconnector1Node1, int disconnector1Node2, + int breaker1Node1, int breaker1Node2, int disconnector2Node1, int disconnector2Node2) { + Substation substation1 = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel1 = substation1.newVoltageLevel() + .setId("VoltageLevel1") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + voltageLevel1.getNodeBreakerView().setNodeCount(20); + voltageLevel1.getNodeBreakerView().newBusbarSection() + .setId("BusbarSection") + .setNode(busbarNode) + .add(); + voltageLevel1.newGenerator() + .setId("Generator") + .setNode(generatorNode) + .setTargetP(100) + .setTargetV(380) + .setVoltageRegulatorOn(true) + .setMaxP(100) + .setMinP(0) + .add(); + voltageLevel1.getNodeBreakerView().newDisconnector() + .setId("Disconnector1") + .setNode1(disconnector1Node1) + .setNode2(disconnector1Node2) + .add(); + voltageLevel1.getNodeBreakerView().newBreaker() + .setId("Breaker1") + .setNode1(breaker1Node1) + .setNode2(breaker1Node2) + .add(); + voltageLevel1.getNodeBreakerView().newDisconnector() + .setId("Disconnector2") + .setNode1(disconnector2Node1) + .setNode2(disconnector2Node2) + .add(); + return voltageLevel1; + } + + protected void createSecondVoltageLevel(Network network) { + Substation substation2 = network.newSubstation() + .setId("Substation2") + .setCountry(Country.FR) + .add(); + VoltageLevel voltageLevel2 = substation2.newVoltageLevel() + .setId("VoltageLevel2") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + voltageLevel2.getNodeBreakerView() + .setNodeCount(10); + } + + protected void createLine(Network network, int lineNode) { + network.newLine() + .setId("Line") + .setVoltageLevel1("VoltageLevel1") + .setNode1(lineNode) + .setVoltageLevel2("VoltageLevel2") + .setNode2(0) + .setR(3.0) + .setX(33.0) + .setG1(0.0) + .setB1(386E-6 / 2) + .setG2(0.0) + .setB2(386E-6 / 2) + .add(); + } + + protected abstract void addDiagramData(Network network); + + protected void addBusbarSectionDiagramData(BusbarSection busbarSection, DiagramPoint point1, DiagramPoint point2) { + NodeDiagramData busbarDiagramData = new NodeDiagramData<>(busbarSection); + NodeDiagramData.NodeDiagramDataDetails diagramDetails = busbarDiagramData.new NodeDiagramDataDetails(); + diagramDetails.setPoint1(point1); + diagramDetails.setPoint2(point2); + busbarDiagramData.addData(DIAGRAM_NAME, diagramDetails); + busbarSection.addExtension(NodeDiagramData.class, busbarDiagramData); + } + + protected void addGeneratorDiagramData(Generator generator, DiagramPoint generatorPoint, DiagramPoint terminalPoint1, DiagramPoint terminalPoint2) { + InjectionDiagramData generatorDiagramData = new InjectionDiagramData<>(generator); + InjectionDiagramData.InjectionDiagramDetails diagramDetails = generatorDiagramData.new InjectionDiagramDetails(generatorPoint, 0); + diagramDetails.addTerminalPoint(terminalPoint1); + diagramDetails.addTerminalPoint(terminalPoint2); + generatorDiagramData.addData(DIAGRAM_NAME, diagramDetails); + generator.addExtension(InjectionDiagramData.class, generatorDiagramData); + } + + protected void addSwitchDiagramData(Switch sw, DiagramPoint switchPoint, int rotation, DiagramPoint terminal1Point1, DiagramPoint terminal1Point2, + DiagramPoint terminal2Point1, DiagramPoint terminal2Point2) { + CouplingDeviceDiagramData switchDiagramData = new CouplingDeviceDiagramData<>(sw); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails diagramDetails = switchDiagramData.new CouplingDeviceDiagramDetails(switchPoint, rotation); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, terminal1Point1); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, terminal1Point2); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, terminal2Point1); + diagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, terminal2Point2); + switchDiagramData.addData(DIAGRAM_NAME, diagramDetails); + sw.addExtension(CouplingDeviceDiagramData.class, switchDiagramData); + } + + protected void addLineDiagramData(Line line, DiagramPoint point1, DiagramPoint point2) { + LineDiagramData lineDiagramData = new LineDiagramData<>(line); + lineDiagramData.addPoint(DIAGRAM_NAME, point1); + lineDiagramData.addPoint(DIAGRAM_NAME, point2); + line.addExtension(LineDiagramData.class, lineDiagramData); + } + + @Override + protected void checkGraph(Graph graph) { + assertEquals(6, graph.getNodes().size()); + + assertEquals(Node.NodeType.BUS, graph.getNodes().get(0).getType()); + assertEquals(Node.NodeType.FEEDER, graph.getNodes().get(1).getType()); + assertEquals(Node.NodeType.FEEDER, graph.getNodes().get(2).getType()); + assertEquals(Node.NodeType.SWITCH, graph.getNodes().get(3).getType()); + assertEquals(Node.NodeType.SWITCH, graph.getNodes().get(4).getType()); + assertEquals(Node.NodeType.SWITCH, graph.getNodes().get(5).getType()); + + assertEquals("BusbarSection", graph.getNodes().get(0).getId()); + assertEquals("Line_ONE", graph.getNodes().get(1).getId()); + assertEquals("Generator", graph.getNodes().get(2).getId()); + assertEquals("Disconnector1", graph.getNodes().get(3).getId()); + assertEquals("Breaker1", graph.getNodes().get(4).getId()); + assertEquals("Disconnector2", graph.getNodes().get(5).getId()); + + assertEquals(BUSBAR_SECTION, graph.getNodes().get(0).getComponentType()); + assertEquals(LINE, graph.getNodes().get(1).getComponentType()); + assertEquals(GENERATOR, graph.getNodes().get(2).getComponentType()); + assertEquals(DISCONNECTOR, graph.getNodes().get(3).getComponentType()); + assertEquals(BREAKER, graph.getNodes().get(4).getComponentType()); + assertEquals(DISCONNECTOR, graph.getNodes().get(5).getComponentType()); + + assertEquals(2, graph.getNodes().get(0).getAdjacentNodes().size()); + checkAdjacentNodes(graph.getNodes().get(0), Arrays.asList("Disconnector1", "Generator")); + assertEquals(1, graph.getNodes().get(1).getAdjacentNodes().size()); + assertEquals("Disconnector2", graph.getNodes().get(1).getAdjacentNodes().get(0).getId()); + assertEquals(1, graph.getNodes().get(2).getAdjacentNodes().size()); + assertEquals("BusbarSection", graph.getNodes().get(2).getAdjacentNodes().get(0).getId()); + assertEquals(2, graph.getNodes().get(3).getAdjacentNodes().size()); + checkAdjacentNodes(graph.getNodes().get(3), Arrays.asList("BusbarSection", "Breaker1")); + assertEquals(2, graph.getNodes().get(4).getAdjacentNodes().size()); + checkAdjacentNodes(graph.getNodes().get(4), Arrays.asList("Disconnector1", "Disconnector2")); + assertEquals(2, graph.getNodes().get(5).getAdjacentNodes().size()); + checkAdjacentNodes(graph.getNodes().get(5), Arrays.asList("Breaker1", "Line_ONE")); + + assertEquals(5, graph.getEdges().size()); + } + + @Test + public void test() { + test(voltageLevel); + } + + @Test + public void testWithInternalConnections() { + test(voltageLevelWithInternalConnections); + } + +} diff --git a/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/BusTopologyTest.java b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/BusTopologyTest.java new file mode 100644 index 000000000..e593bd867 --- /dev/null +++ b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/BusTopologyTest.java @@ -0,0 +1,319 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import static com.powsybl.sld.library.ComponentTypeName.BUSBAR_SECTION; +import static com.powsybl.sld.library.ComponentTypeName.CAPACITOR; +import static com.powsybl.sld.library.ComponentTypeName.DANGLING_LINE; +import static com.powsybl.sld.library.ComponentTypeName.LOAD; +import static com.powsybl.sld.library.ComponentTypeName.STATIC_VAR_COMPENSATOR; +import static com.powsybl.sld.library.ComponentTypeName.TWO_WINDINGS_TRANSFORMER; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import com.powsybl.cgmes.iidm.extensions.dl.*; +import org.junit.Before; +import org.junit.Test; + +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Country; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.iidm.network.StaticVarCompensator; +import com.powsybl.iidm.network.Substation; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.model.BusNode; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.Node; +import com.powsybl.sld.model.SubstationGraph; + +/** + * + * @author Massimo Ferraro + */ +public class BusTopologyTest extends AbstractCgmesVoltageLevelLayoutTest { + + private VoltageLevel voltageLevel; + private Substation substation; + private VoltageLevel voltageLevel2; + + @Before + public void setUp() { + createNetwork(); + } + + private void createNetwork() { + Network network = Network.create("test", "test"); + substation = network.newSubstation() + .setId("Substation") + .setCountry(Country.FR) + .add(); + voltageLevel = createFirstVoltageLevel(substation); + voltageLevel2 = createSecondVoltageLevel(substation); + createTransformer(substation); + addDiagramData(network); + } + + private VoltageLevel createFirstVoltageLevel(Substation substation) { + VoltageLevel voltageLevel1 = substation.newVoltageLevel() + .setId("VoltageLevel1") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel1.getBusBreakerView().newBus() + .setId("Bus1") + .add(); + voltageLevel1.newLoad() + .setId("Load") + .setBus("Bus1") + .setConnectableBus("Bus1") + .setP0(100) + .setQ0(50) + .add(); + voltageLevel1.newShuntCompensator() + .setId("Shunt") + .setBus("Bus1") + .setConnectableBus("Bus1") + .setbPerSection(1e-5) + .setCurrentSectionCount(1) + .setMaximumSectionCount(1) + .add(); + voltageLevel1.newDanglingLine() + .setId("DanglingLine") + .setBus("Bus1") + .setR(10.0) + .setX(1.0) + .setB(10e-6) + .setG(10e-5) + .setP0(50.0) + .setQ0(30.0) + .add(); + return voltageLevel1; + } + + private VoltageLevel createSecondVoltageLevel(Substation substation) { + VoltageLevel voltageLevel2 = substation.newVoltageLevel() + .setId("VoltageLevel2") + .setNominalV(400) + .setTopologyKind(TopologyKind.BUS_BREAKER) + .add(); + voltageLevel2.getBusBreakerView().newBus() + .setId("Bus2") + .add(); + voltageLevel2.newStaticVarCompensator() + .setId("Svc") + .setConnectableBus("Bus2") + .setBus("Bus2") + .setBmin(0.0002) + .setBmax(0.0008) + .setRegulationMode(StaticVarCompensator.RegulationMode.VOLTAGE) + .setVoltageSetPoint(390.0) + .setReactivePowerSetPoint(1.0) + .add(); + return voltageLevel2; + } + + private void createTransformer(Substation substation) { + int zb380 = 380 * 380 / 100; + substation.newTwoWindingsTransformer() + .setId("Transformer") + .setVoltageLevel1("VoltageLevel1") + .setBus1("Bus1") + .setConnectableBus1("Bus1") + .setRatedU1(24.0) + .setVoltageLevel2("VoltageLevel2") + .setBus2("Bus2") + .setConnectableBus2("Bus2") + .setRatedU2(400.0) + .setR(0.24 / 1300 * zb380) + .setX(Math.sqrt(10 * 10 - 0.24 * 0.24) / 1300 * zb380) + .setG(0.0) + .setB(0.0) + .add(); + } + + private void addDiagramData(Network network) { + Bus bus = voltageLevel.getBusBreakerView().getBus("Bus1"); + NodeDiagramData busDiagramData = new NodeDiagramData<>(bus); + NodeDiagramData.NodeDiagramDataDetails diagramDetails = busDiagramData.new NodeDiagramDataDetails(); + diagramDetails.setPoint1(new DiagramPoint(60, 10, 1)); + diagramDetails.setPoint2(new DiagramPoint(60, 70, 2)); + busDiagramData.addData(DIAGRAM_NAME, diagramDetails); + bus.addExtension(NodeDiagramData.class, busDiagramData); + + Load load = network.getLoad("Load"); + InjectionDiagramData loadDiagramData = new InjectionDiagramData<>(load); + InjectionDiagramData.InjectionDiagramDetails loadsDiagramDetails = loadDiagramData.new InjectionDiagramDetails(new DiagramPoint(10, 20, 0), 90); + loadsDiagramDetails.addTerminalPoint(new DiagramPoint(15, 20, 2)); + loadsDiagramDetails.addTerminalPoint(new DiagramPoint(60, 20, 1)); + loadDiagramData.addData(DIAGRAM_NAME, loadsDiagramDetails); + load.addExtension(InjectionDiagramData.class, loadDiagramData); + + ShuntCompensator shunt = network.getShuntCompensator("Shunt"); + InjectionDiagramData shuntDiagramData = new InjectionDiagramData<>(shunt); + InjectionDiagramData.InjectionDiagramDetails shuntDiagramDetails = shuntDiagramData.new InjectionDiagramDetails(new DiagramPoint(15, 55, 0), 90); + shuntDiagramDetails.addTerminalPoint(new DiagramPoint(20, 55, 1)); + shuntDiagramDetails.addTerminalPoint(new DiagramPoint(60, 55, 2)); + shuntDiagramData.addData(DIAGRAM_NAME, shuntDiagramDetails); + shunt.addExtension(InjectionDiagramData.class, shuntDiagramData); + + DanglingLine danglingLine = network.getDanglingLine("DanglingLine"); + LineDiagramData danglingLineDiagramData = new LineDiagramData<>(danglingLine); + danglingLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(60, 60, 1)); + danglingLineDiagramData.addPoint(DIAGRAM_NAME, new DiagramPoint(120, 60, 2)); + danglingLine.addExtension(LineDiagramData.class, danglingLineDiagramData); + + TwoWindingsTransformer twt = network.getTwoWindingsTransformer("Transformer"); + CouplingDeviceDiagramData twtDiagramData = new CouplingDeviceDiagramData<>(twt); + CouplingDeviceDiagramData.CouplingDeviceDiagramDetails twtDiagramDetails = twtDiagramData.new CouplingDeviceDiagramDetails(new DiagramPoint(100, 15, 0), 90); + twtDiagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, new DiagramPoint(95, 15, 1)); + twtDiagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL1, new DiagramPoint(60, 15, 2)); + twtDiagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, new DiagramPoint(105, 15, 1)); + twtDiagramDetails.addTerminalPoint(DiagramTerminal.TERMINAL2, new DiagramPoint(120, 15, 2)); + twtDiagramData.addData(DIAGRAM_NAME, twtDiagramDetails); + twt.addExtension(CouplingDeviceDiagramData.class, twtDiagramData); + + Bus bus2 = voltageLevel2.getBusBreakerView().getBus("Bus2"); + NodeDiagramData bus2DiagramData = new NodeDiagramData<>(bus2); + NodeDiagramData.NodeDiagramDataDetails diagramDetails2 = bus2DiagramData.new NodeDiagramDataDetails(); + diagramDetails2.setPoint1(new DiagramPoint(120, 10, 1)); + diagramDetails2.setPoint2(new DiagramPoint(120, 25, 2)); + bus2DiagramData.addData(DIAGRAM_NAME, diagramDetails2); + bus2.addExtension(NodeDiagramData.class, bus2DiagramData); + + StaticVarCompensator svc = network.getStaticVarCompensator("Svc"); + InjectionDiagramData svcDiagramData = new InjectionDiagramData<>(svc); + InjectionDiagramData.InjectionDiagramDetails svcDiagramDataDetails = svcDiagramData.new InjectionDiagramDetails(new DiagramPoint(140, 15, 0), 270); + svcDiagramDataDetails.addTerminalPoint(new DiagramPoint(135, 15, 1)); + svcDiagramDataDetails.addTerminalPoint(new DiagramPoint(120, 15, 2)); + svcDiagramData.addData(DIAGRAM_NAME, svcDiagramDataDetails); + svc.addExtension(InjectionDiagramData.class, svcDiagramData); + + NetworkDiagramData.addDiagramName(network, DIAGRAM_NAME); + } + + @Test + public void testVoltageLevelLayout() { + test(voltageLevel); + } + + @Test + public void testSubstationLayout() { + SubstationGraph graph = SubstationGraph.create(substation); + LayoutParameters layoutParameters = new LayoutParameters(); + layoutParameters.setScaleFactor(2); + layoutParameters.setDiagramName(DIAGRAM_NAME); + new CgmesSubstationLayout(graph).run(layoutParameters); + checkGraph(graph.getNode(voltageLevel.getId())); + checkCoordinates(graph.getNode(voltageLevel.getId())); + checkGraphVl2(graph.getNode(voltageLevel2.getId())); + checkCoordinatesVl2(graph.getNode(voltageLevel2.getId())); + } + + @Override + protected void checkGraph(Graph graph) { + assertEquals(5, graph.getNodes().size()); + + assertEquals(Node.NodeType.BUS, graph.getNodes().get(0).getType()); + assertEquals(Node.NodeType.FEEDER, graph.getNodes().get(1).getType()); + assertEquals(Node.NodeType.FEEDER, graph.getNodes().get(2).getType()); + assertEquals(Node.NodeType.FEEDER, graph.getNodes().get(3).getType()); + assertEquals(Node.NodeType.FEEDER, graph.getNodes().get(4).getType()); + + assertEquals("Bus1", graph.getNodes().get(0).getId()); + assertEquals("Load", graph.getNodes().get(1).getId()); + assertEquals("Shunt", graph.getNodes().get(2).getId()); + assertEquals("DanglingLine", graph.getNodes().get(3).getId()); + assertEquals("Transformer_ONE", graph.getNodes().get(4).getId()); + + assertEquals(BUSBAR_SECTION, graph.getNodes().get(0).getComponentType()); + assertEquals(LOAD, graph.getNodes().get(1).getComponentType()); + assertEquals(CAPACITOR, graph.getNodes().get(2).getComponentType()); + assertEquals(DANGLING_LINE, graph.getNodes().get(3).getComponentType()); + assertEquals(TWO_WINDINGS_TRANSFORMER, graph.getNodes().get(4).getComponentType()); + + assertEquals(4, graph.getNodes().get(0).getAdjacentNodes().size()); + checkAdjacentNodes(graph.getNodes().get(0), Arrays.asList("Load", "Shunt", "DanglingLine", "Transformer_ONE")); + checkBusConnection(graph.getNodes().get(1)); + checkBusConnection(graph.getNodes().get(2)); + checkBusConnection(graph.getNodes().get(3)); + checkBusConnection(graph.getNodes().get(4)); + + assertEquals(4, graph.getEdges().size()); + } + + private void checkBusConnection(Node node) { + assertEquals(1, node.getAdjacentNodes().size()); + assertEquals("Bus1", node.getAdjacentNodes().get(0).getId()); + } + + @Override + protected void checkCoordinates(Graph graph) { + assertEquals(120, graph.getNodes().get(0).getX(), 0); + assertEquals(10, graph.getNodes().get(0).getY(), 0); + assertEquals(120, ((BusNode) graph.getNodes().get(0)).getPxWidth(), 0); + assertTrue(graph.getNodes().get(0).isRotated()); + assertEquals(20, graph.getNodes().get(1).getX(), 0); + assertEquals(30, graph.getNodes().get(1).getY(), 0); + assertTrue(graph.getNodes().get(1).isRotated()); + assertEquals(30, graph.getNodes().get(2).getX(), 0); + assertEquals(100, graph.getNodes().get(2).getY(), 0); + assertTrue(graph.getNodes().get(2).isRotated()); + assertEquals(160, graph.getNodes().get(3).getX(), 0); + assertEquals(110, graph.getNodes().get(3).getY(), 0); + assertTrue(graph.getNodes().get(3).isRotated()); + assertEquals(200, graph.getNodes().get(4).getX(), 0); + assertEquals(20, graph.getNodes().get(4).getY(), 0); + assertFalse(graph.getNodes().get(4).isRotated()); + } + + private void checkGraphVl2(Graph graph) { + assertEquals(3, graph.getNodes().size()); + + assertEquals(Node.NodeType.BUS, graph.getNodes().get(0).getType()); + assertEquals(Node.NodeType.FEEDER, graph.getNodes().get(1).getType()); + assertEquals(Node.NodeType.FEEDER, graph.getNodes().get(2).getType()); + + assertEquals("Bus2", graph.getNodes().get(0).getId()); + assertEquals("Svc", graph.getNodes().get(1).getId()); + assertEquals("Transformer_TWO", graph.getNodes().get(2).getId()); + + assertEquals(BUSBAR_SECTION, graph.getNodes().get(0).getComponentType()); + assertEquals(STATIC_VAR_COMPENSATOR, graph.getNodes().get(1).getComponentType()); + assertEquals(TWO_WINDINGS_TRANSFORMER, graph.getNodes().get(2).getComponentType()); + + assertEquals(2, graph.getNodes().get(0).getAdjacentNodes().size()); + assertEquals("Svc", graph.getNodes().get(0).getAdjacentNodes().get(0).getId()); + assertEquals("Transformer_TWO", graph.getNodes().get(0).getAdjacentNodes().get(1).getId()); + assertEquals(1, graph.getNodes().get(1).getAdjacentNodes().size()); + assertEquals("Bus2", graph.getNodes().get(1).getAdjacentNodes().get(0).getId()); + assertEquals(1, graph.getNodes().get(2).getAdjacentNodes().size()); + assertEquals("Bus2", graph.getNodes().get(2).getAdjacentNodes().get(0).getId()); + } + + protected void checkCoordinatesVl2(Graph graph) { + assertEquals(240, graph.getNodes().get(0).getX(), 0); + assertEquals(10, graph.getNodes().get(0).getY(), 0); + assertEquals(30, ((BusNode) graph.getNodes().get(0)).getPxWidth(), 0); + assertTrue(graph.getNodes().get(0).isRotated()); + assertEquals(280, graph.getNodes().get(1).getX(), 0); + assertEquals(20, graph.getNodes().get(1).getY(), 0); + assertTrue(graph.getNodes().get(1).isRotated()); + assertEquals(200, graph.getNodes().get(2).getX(), 0); + assertEquals(20, graph.getNodes().get(2).getY(), 0); + assertFalse(graph.getNodes().get(2).isRotated()); + } + +} diff --git a/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/LayoutToCgmesExtensionsTest.java b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/LayoutToCgmesExtensionsTest.java new file mode 100644 index 000000000..d0456b16f --- /dev/null +++ b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/LayoutToCgmesExtensionsTest.java @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import com.powsybl.cgmes.iidm.Networks; +import com.powsybl.cgmes.iidm.extensions.dl.*; +import com.powsybl.iidm.network.*; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * @author Christian Biasuzzi + */ +public class LayoutToCgmesExtensionsTest { + + private List networks = new ArrayList<>(); + + @Before + public void setUp() { + createNetworks(); + } + + private void createNetworks() { + networks.add(Networks.createNetworkWithGenerator()); + networks.add(Networks.createNetworkWithLine()); + networks.add(Networks.createNetworkWithDanglingLine()); + networks.add(Networks.createNetworkWithBusbar()); + networks.add(Networks.createNetworkWithBus()); + networks.add(Networks.createNetworkWithLoad()); + networks.add(Networks.createNetworkWithShuntCompensator()); + networks.add(Networks.createNetworkWithStaticVarCompensator()); + networks.add(Networks.createNetworkWithSwitch()); + networks.add(Networks.createNetworkWithTwoWindingsTransformer()); + networks.add(Networks.createNetworkWithThreeWindingsTransformer()); + networks.add(Networks.createNetworkWithBusbarAndSwitch()); + } + + private void checkExtensionsSet(Network network) { + network.getVoltageLevelStream().forEach(vl -> { + vl.visitEquipments(new DefaultTopologyVisitor() { + @Override + public void visitDanglingLine(DanglingLine danglingLine) { + assertNotNull(danglingLine.getExtension(LineDiagramData.class)); + } + + @Override + public void visitGenerator(Generator generator) { + assertNotNull(generator.getExtension(InjectionDiagramData.class)); + } + + @Override + public void visitShuntCompensator(ShuntCompensator sc) { + assertNotNull(sc.getExtension(InjectionDiagramData.class)); + } + + @Override + public void visitLoad(Load load) { + assertNotNull(load.getExtension(InjectionDiagramData.class)); + } + + @Override + public void visitStaticVarCompensator(StaticVarCompensator staticVarCompensator) { + assertNotNull(staticVarCompensator.getExtension(InjectionDiagramData.class)); + } + + @Override + public void visitLine(Line line, Branch.Side side) { + assertNotNull(line.getExtension(LineDiagramData.class)); + } + + @Override + public void visitBusbarSection(BusbarSection busBarSection) { + assertNotNull(busBarSection.getExtension(NodeDiagramData.class)); + } + + @Override + public void visitTwoWindingsTransformer(TwoWindingsTransformer transformer, Branch.Side side) { + assertNotNull(transformer.getExtension(CouplingDeviceDiagramData.class)); + } + + @Override + public void visitThreeWindingsTransformer(ThreeWindingsTransformer transformer, ThreeWindingsTransformer.Side side) { + assertNotNull(transformer.getExtension(ThreeWindingsTransformerDiagramData.class)); + } + + @Override + public void visitHvdcConverterStation(HvdcConverterStation converterStation) { + assertNotNull(converterStation.getExtension(LineDiagramData.class)); + } + }); + }); + } + + private void checkExtensionsUnset(Network network) { + network.getVoltageLevelStream().forEach(vl -> { + vl.visitEquipments(new DefaultTopologyVisitor() { + @Override + public void visitDanglingLine(DanglingLine danglingLine) { + assertNull(danglingLine.getExtension(LineDiagramData.class)); + } + + @Override + public void visitGenerator(Generator generator) { + assertNull(generator.getExtension(InjectionDiagramData.class)); + } + + @Override + public void visitShuntCompensator(ShuntCompensator sc) { + assertNull(sc.getExtension(InjectionDiagramData.class)); + } + + @Override + public void visitLoad(Load load) { + assertNull(load.getExtension(InjectionDiagramData.class)); + } + + @Override + public void visitStaticVarCompensator(StaticVarCompensator staticVarCompensator) { + assertNull(staticVarCompensator.getExtension(InjectionDiagramData.class)); + } + + @Override + public void visitLine(Line line, Branch.Side side) { + assertNull(line.getExtension(LineDiagramData.class)); + } + + @Override + public void visitBusbarSection(BusbarSection busBarSection) { + assertNull(busBarSection.getExtension(NodeDiagramData.class)); + } + + @Override + public void visitTwoWindingsTransformer(TwoWindingsTransformer transformer, Branch.Side side) { + assertNull(transformer.getExtension(CouplingDeviceDiagramData.class)); + } + + @Override + public void visitThreeWindingsTransformer(ThreeWindingsTransformer transformer, ThreeWindingsTransformer.Side side) { + assertNull(transformer.getExtension(ThreeWindingsTransformerDiagramData.class)); + } + + @Override + public void visitHvdcConverterStation(HvdcConverterStation converterStation) { + assertNull(converterStation.getExtension(LineDiagramData.class)); + } + }); + }); + } + + @Test + public void testCgmesDlExtensionsEmpty() { + networks.stream().forEach(this::checkExtensionsUnset); + } + + @Test + public void testCgmesDlExtensionsSet() { + networks.stream().forEach(network -> { + LayoutToCgmesExtensionsConverter lconv = new LayoutToCgmesExtensionsConverter(); + lconv.convertLayout(network, "new-diagram"); + checkExtensionsSet(network); + }); + } + + @Test + public void testCgmesDlExtensionsSetNoname() { + networks.stream().forEach(network -> { + LayoutToCgmesExtensionsConverter lconv = new LayoutToCgmesExtensionsConverter(); + lconv.convertLayout(network); + checkExtensionsSet(network); + }); + } + + @Test + public void testCgmesDlExtensionsEmptyNetwork() { + Network network = NetworkFactory.findDefault().createNetwork("testEmpty", "testEmpty"); + LayoutToCgmesExtensionsConverter lconv = new LayoutToCgmesExtensionsConverter(); + lconv.convertLayout(network, null); + + checkExtensionsUnset(network); + } + +} diff --git a/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/NodeTopologyHorizontalBusbarTest.java b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/NodeTopologyHorizontalBusbarTest.java new file mode 100644 index 000000000..e76c711b8 --- /dev/null +++ b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/NodeTopologyHorizontalBusbarTest.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.iidm.network.Network; +import com.powsybl.sld.model.BusNode; +import com.powsybl.sld.model.Graph; + +/** + * + * @author Massimo Ferraro + */ +public class NodeTopologyHorizontalBusbarTest extends AbstractNodeTopologyTest { + + @Override + protected void addDiagramData(Network network) { + addBusbarSectionDiagramData(network.getBusbarSection("BusbarSection"), new DiagramPoint(20, 115, 1), new DiagramPoint(180, 115, 2)); + addGeneratorDiagramData(network.getGenerator("Generator"), new DiagramPoint(105, 230, 0), + new DiagramPoint(105, 225, 1), new DiagramPoint(105, 115, 2)); + addSwitchDiagramData(network.getSwitch("Disconnector1"), new DiagramPoint(105, 100, 0), 0, new DiagramPoint(105, 95, 1), + new DiagramPoint(105, 90, 2), new DiagramPoint(105, 105, 1), new DiagramPoint(105, 115, 2)); + addSwitchDiagramData(network.getSwitch("Breaker1"), new DiagramPoint(105, 80, 0), 0, new DiagramPoint(105, 85, 1), + new DiagramPoint(105, 90, 2), new DiagramPoint(105, 75, 1), new DiagramPoint(105, 70, 2)); + addSwitchDiagramData(network.getSwitch("Disconnector2"), new DiagramPoint(105, 60, 0), 0, new DiagramPoint(105, 65, 1), + new DiagramPoint(105, 70, 2), new DiagramPoint(105, 55, 1), new DiagramPoint(105, 50, 2)); + addLineDiagramData(network.getLine("Line"), new DiagramPoint(105, 50, 1), new DiagramPoint(105, 10, 2)); + } + + @Override + protected void checkCoordinates(Graph graph) { + assertEquals(20, graph.getNodes().get(0).getX(), 0); + assertEquals(140, graph.getNodes().get(0).getY(), 0); + assertEquals(320, ((BusNode) graph.getNodes().get(0)).getPxWidth(), 0); + assertFalse(graph.getNodes().get(0).isRotated()); + assertEquals(190, graph.getNodes().get(1).getX(), 0); + assertEquals(10, graph.getNodes().get(1).getY(), 0); + assertEquals(190, graph.getNodes().get(2).getX(), 0); + assertEquals(370, graph.getNodes().get(2).getY(), 0); + assertEquals(190, graph.getNodes().get(3).getX(), 0); + assertEquals(110, graph.getNodes().get(3).getY(), 0); + assertFalse(graph.getNodes().get(3).isRotated()); + assertEquals(190, graph.getNodes().get(4).getX(), 0); + assertEquals(70, graph.getNodes().get(4).getY(), 0); + assertFalse(graph.getNodes().get(4).isRotated()); + assertEquals(190, graph.getNodes().get(5).getX(), 0); + assertEquals(30, graph.getNodes().get(5).getY(), 0); + assertFalse(graph.getNodes().get(5).isRotated()); + } + +} diff --git a/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/NodeTopologyVerticalBusbarTest.java b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/NodeTopologyVerticalBusbarTest.java new file mode 100644 index 000000000..47e38d47a --- /dev/null +++ b/single-line-diagram-cgmes/src/test/java/com/powsybl/sld/cgmes/NodeTopologyVerticalBusbarTest.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.cgmes; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.powsybl.cgmes.iidm.extensions.dl.DiagramPoint; +import com.powsybl.iidm.network.Network; +import com.powsybl.sld.model.BusNode; +import com.powsybl.sld.model.Graph; + +/** + * + * @author Massimo Ferraro + */ +public class NodeTopologyVerticalBusbarTest extends AbstractNodeTopologyTest { + + @Override + protected void addDiagramData(Network network) { + addBusbarSectionDiagramData(network.getBusbarSection("BusbarSection"), new DiagramPoint(140, 60, 1), new DiagramPoint(140, 170, 2)); + addGeneratorDiagramData(network.getGenerator("Generator"), new DiagramPoint(45, 85, 0), + new DiagramPoint(50, 85, 1), new DiagramPoint(140, 85, 2)); + addSwitchDiagramData(network.getSwitch("Disconnector1"), new DiagramPoint(155, 150, 0), 90, new DiagramPoint(150, 150, 1), + new DiagramPoint(145, 150, 2), new DiagramPoint(130, 160, 1), new DiagramPoint(165, 150, 2)); + addSwitchDiagramData(network.getSwitch("Breaker1"), new DiagramPoint(175, 150, 0), 90, new DiagramPoint(170, 150, 1), + new DiagramPoint(165, 150, 2), new DiagramPoint(180, 150, 1), new DiagramPoint(185, 150, 2)); + addSwitchDiagramData(network.getSwitch("Disconnector2"), new DiagramPoint(195, 150, 0), 90, new DiagramPoint(190, 150, 1), + new DiagramPoint(185, 150, 2), new DiagramPoint(200, 150, 1), new DiagramPoint(205, 150, 1)); + addLineDiagramData(network.getLine("Line"), new DiagramPoint(205, 150, 1), new DiagramPoint(260, 150, 2)); + } + + @Override + protected void checkCoordinates(Graph graph) { + assertEquals(210, graph.getNodes().get(0).getX(), 0); + assertEquals(10, graph.getNodes().get(0).getY(), 0); + assertEquals(220, ((BusNode) graph.getNodes().get(0)).getPxWidth(), 0); + assertTrue(graph.getNodes().get(0).isRotated()); + assertEquals(340, graph.getNodes().get(1).getX(), 0); + assertEquals(190, graph.getNodes().get(1).getY(), 0); + assertEquals(20, graph.getNodes().get(2).getX(), 0); + assertEquals(60, graph.getNodes().get(2).getY(), 0); + assertEquals(240, graph.getNodes().get(3).getX(), 0); + assertEquals(190, graph.getNodes().get(3).getY(), 0); + assertTrue(graph.getNodes().get(3).isRotated()); + assertEquals(280, graph.getNodes().get(4).getX(), 0); + assertEquals(190, graph.getNodes().get(4).getY(), 0); + assertTrue(graph.getNodes().get(4).isRotated()); + assertEquals(320, graph.getNodes().get(5).getX(), 0); + assertEquals(190, graph.getNodes().get(5).getY(), 0); + assertTrue(graph.getNodes().get(5).isRotated()); + } + +} diff --git a/single-line-diagram-core/doc/CellBlockDecomposer.adoc b/single-line-diagram-core/doc/CellBlockDecomposer.adoc new file mode 100644 index 000000000..440d0a229 --- /dev/null +++ b/single-line-diagram-core/doc/CellBlockDecomposer.adoc @@ -0,0 +1,6 @@ +== Structure the cells into `Blocks` +Decompose each cell into `Block` and merge into the Cell Root Block + + + Blocks are the building elements that are assembled hierarchically to organise the layout of the cells. + +when a busbar on one vertical position is spanning over many busbars having another vertical position. diff --git a/single-line-diagram-core/doc/CellDetector.adoc b/single-line-diagram-core/doc/CellDetector.adoc new file mode 100644 index 000000000..3e5239993 --- /dev/null +++ b/single-line-diagram-core/doc/CellDetector.adoc @@ -0,0 +1,128 @@ +== Detect the cells + +=== Definitions and goal +An object of `Cell` class represents a connected subgraph of nodes that participate to a common purpose. The goal of the cell detection is to create these subgraphs that will then: + +* structures the organisation of the busbars layout +* be displayed according to their types + +Two subclasses inherit the `Cell` class : + +* `InternCell` for cells that does not contain `FEEDER` nodes and are not shunting `ExternCell` cells, +* `ExternCell` for cells connecting `FEEDER` nodes to `BUS` nodes. + +Cells can be of one of the following enum `CellType`: + +`INTERN`:: +The smallest subGraph delimited by `BUS` nodes (ie not including `FEEDER`). + +Such a cell instanciates the `InternCell` subclass + +`INTERNBOUND`:: +A singular intern Cell having exactly 3 nodes: 1 `SWITCH` node connecting 2 neighboring (ie having the same vertical position, and having contiguous horizontal values) `BUS` nodes + +(note that the cellDetector will detect this as `INTERN`, further processing will detect it) +Such a cell instanciates the `InternCell` subclass + + +`EXTERN`:: +The smallest subGraph delimited by `BUS` nodes and `FEEDER` nodes with at least one node having the following property: each branch extracted from this very node ends with nodes of a single `nodeType` among: `BUS` or `FEEDER` or `SHUNT` + + Such a cell instanciates the `ExternCell` subclass + +`SHUNT`:: +A path between 2 `FictitiousNode` nodes of 2 `ExternCell` cells + +Such cell instanciates the `Cell` class + +`UNDEFINED`:: +Initial type, should not stay as is. + +The figure shows examples of cells of each `CellType`. + +.CellTypes enum +image::images/CellTypes.svg[align="center"] + +=== Implementations +==== The `ImplicitCellDetector` class +The ImplicitCellDetector implements an algorithm sticking to the above definitions. + +===== Cleaning +A preliminary step consists in calling some cleaning and tuning operations of the `Graph` class. + +* `Graph.removeUnnecessaryFictitiousNodes()` is _optionally_ called to simplify the graph by removing redundant `FICTITIOUS` nodes +* `Graph.extendFeederWithMultipleSwitches()` is _systematically_ called to insert a `FICTITIOUS` node to a `FEEDER` node when it is originally connected to many `SWITCH` nodes as graphically, a `FeederNode` shall be connected to one and only one node. +* `Graph.extendFirstOutsideNode()` copes with the case of a string of 2+ `SWITCH` nodes starting from a `BUS` node. In that case a `FICTITIOUS` node need to be applied to align the connection of the second `SWITCH` node at the `LayoutParameters.stackHeight` +* `Graph.extendBreakerConnectedToBus()` copes with a breaker directly connected to the `BUS` node. A fictitious switch is inserted in between. +* `Graph.extendFeederConnectedToBus()` copes with `FEEDER` node directly connected to the `BUS` node. A `FICTITIOUS` node is inserted in between. + +==== Cell detection algorithm +===== Steps + +The algorithm will be explained based on the following graph that would result in the figure displayed to illustrate the cellTypes enum: + +.raw graph +image::images/rawGraph.svg[align="center"] + +* step 1 calls twice the `genericDetectCell` method to identify: +.. the `InternCell` cells +.. other consistent subgraphs that will be analysed in the next steps +* step 2 identifies the consistent subgraphs that are ExternCell +* step 3 tries to separates each consistent subgraph into: +** 2 `ExternCell` cells bound by +** 1 `Cell` cell constituting a shunt + +Steps 1 and 3 make use of the recursive method `rDelimitedExploration` to visit the graph + +CAUTION: Any other pattern is not handled by the algorithm. + +===== the method `rDelimitedExploration` + +`rDelimitedExploration` visits the graph and gathers each nodes of the path to constitute a connected subgraph according to criteria on nodes. Each node visited is added to the `exploredNodes` list that constitutes the list of nodes that shall not be visited again. It takes as parameter two lists of types that will delimits the traversal algorithm : + +* `stopTypes` list: for types that end a current branch +* `exclusionTypes` list: for types that invalidate the current subgraph. + +.Use of the `rDelimitedExploration` method +|==== +|Where |ExclusionTypes |StopTypes +|Step 1 in 1^st^ `genericDetectCell` call to identify the internal cells|`FEEDER` |`BUS` +|Step 1 in 2^nd^ `genericDetectCell` call to identify other consistent subgraphs |- |`FEEDER` and `BUS` +|Step 3 to identify nodes of `SHUNT` nodes|`BUS`, `SHUNT` and `FEEDER` | - +|==== + +===== Step 1: Split the graph and identify `InternCell` cells +The `FEEDER` and `BUS` node types constitute borders of cells or consistent subgraphs. When the algorithm reaches one of them, it either stops the branch, or invalidates the graph traversal. + +`InternCell` cells are easy to determine as being exclusively bordered by `BUS` nodes. + +.step 1 and InternCell +image::images/rawGraphIntern.svg[align="center"] + +==== Step 2: identifies `ExternCell` cells +If one node of the subgraph has each of its branches ending with one single kind of `NodeType` among `BUS` and `FEEDER`, ("_bottleneck_" node in the picture) this is an `ExternCell`. + +Other `ExternCell` cells could be discovered in the next steps when adding the `SHUNT NodeType`. + +.ExternCell detection +image::images/rawGraphExtern.svg[align="center"] + +===== step 3: discriminates `EXTERN` and `SHUNT` cells +To identify the first candidate `SHUNT` node, each `FICTITIOUS` node with more than 3 branches are visited. The expected property of the `SHUNT` node is that: + +. 1+ branch(s) ends with only `BUS` nodes +. 1+ branch(s) ends with only `FEEDER` nodes +. 1 branch is ends with `FEEDER` *and* `BUS` nodes. + +The branches of the first two categories constitutes the first `ExternCell` cell. + +Then the `SHUNT` cell is constituted of: + +* the first `SHUNT` node +* the string of nodes that have only 2 adjacent nodes +* the first node with more than 2 adjacent nodes that becomes the second `SHUNT` node + +Last, the second `ExternCell` cell is build with the second `SHUNT` node and the remaining nodes. + +.Descrimination of a `SHUNT` cell +image::images/rawGraphExternShunt.svg[align="center"] + +''' +==== The `PatternCellDetector` class + +This detector is based on pattern matching algorithm. The patterns are described in `/resources/pattern.xml`. \ No newline at end of file diff --git a/single-line-diagram-core/doc/Index.adoc b/single-line-diagram-core/doc/Index.adoc new file mode 100644 index 000000000..a7c4ff650 --- /dev/null +++ b/single-line-diagram-core/doc/Index.adoc @@ -0,0 +1,23 @@ += Substation diagram core conception +Benoît Jeanson +:toc: + +== package `model` +=== Build the `Graph` + The `VoltageLevel` elements are gathered into graphical components (`Node` and `Edge`) bundled into a `Graph`. + += package `layout` +include::CellDetector.adoc[] + +include::CellBlockDecomposer.adoc[] + +include::PositionFinder.adoc[] + +include::Subsections.adoc[] + +== Horizontal positioning of the cells and horizontal Bus dimension calculation + +== Build Layout into _coordinates_ + += package `svg` +== Elaborate `svg` diff --git a/single-line-diagram-core/doc/PositionFinder.adoc b/single-line-diagram-core/doc/PositionFinder.adoc new file mode 100644 index 000000000..5701be25c --- /dev/null +++ b/single-line-diagram-core/doc/PositionFinder.adoc @@ -0,0 +1,86 @@ +== Position of the Bus nodes and cells order +=== Definitions and goal +Positioning the bus nodes and determination of the external cells order are performed by implementing `PositionFinder` interface. + +The goal is to set: + +* the horizontal and vertical *structural _(h,v)_* `Position` of the bus nodes: `NodeBus.structuralPosition` +* the horizontal order of the cells: `Cell.order` + +The figure hereafter shows the information that are to be established. + +.(h,v) positions of busbars and `ExternCell` cells order +image::images/busbars.svg[align="center"] + +The above structure will be used to explain the different steps of the positioning algorithms. + +This information can be + +* explicitly given (for example to reflect the on site real structure and/or the way the SCADA organises it). One implementation is the `PositionFromExtension` class +* or automatically to fit some layout criteria. One implementation is the `PositionFree` class + +=== Implementations +==== PositionFromExtension: explicit configuration +The `PositionFromExtension` takes the information from the `iidm` extension. +It is gathered: + +* in `Graph.visitBusbarSection()` when building the graph for the NodeBus positions +* and completed in `PositionFromExtension.gatherLayoutExtensionInformation()` for the feeders' order, in which the `Cell.orderFromFeederOrders()` is called to determine the order of the cell. + + + +==== The `PositionFree` class +The goal of the algorithm is to find an organization of the `VoltageLevel` with no other information that the graph itself. This is a "free" positioning since no constraints are given from a configuration source. + +The key criteria that is followed to find a suitable organisation is the fact that each external cells, and any "leg" of an internal cell can be "stacked" - meaning, that all the corresponding busbars are aligned in parallel to be able to connect them with a vertical string of isolators. (This criteria applies only when the structure of the cell allows it - which may not not the case) + +A straightforward approach to this criteria would lead to the following structure, that the algorithm will try to reduce. + +.Raw positionning (the information in gray are not known and will be established by the algorithm) +image::images/rawPosition.svg[align="center"] + + +===== Gathering `VerticalBusConnectionPattern` elements +`VerticalBusConnectionPattern` is na inner class that represents the connection pattern to the busbars as a set of `busNode` for: + +* an external cell +* or a leg of an intern cell. + +Let's call them indistinctly as *cell leg*. + +.verticalBusConnectionPattern of cell legs +image::images/verticalBusConnectionPattern.svg[align="center"] + +This enables to gather all the cell legs having the same pattern as they share the same organisation constraint. + +The possible `VerticalBusConnectionPatterns` are gathered as the key of the `vbcpToCells` map, the value being the list of `ExternCell` cells having this pattern (no need to gather internal cells). + +When the `vbcpToCells` map is built, if a pattern is fully included or fully embraces an already existing `vbcp`, they are merged into the embracing one. For example, the pattern (3) is included in the pattern (3,5). + +CAUTION: Different merges of vbcp can occurs if the structure is not visited in the same order: for example if 3 external cells have (1,2) (1,3) and (1) as vbcp: the cell having the vbcp (1) could be included either into the (1,2) or (1,3) vbcp. + +===== Structuring with internal cells +If the two legs of an internal cell are fully included into a single known vbcp, then this cell is considered as vertical and is not structuring. Indeed, one cell leg already constraints all the busbars of the cell to be parallel. + +This would lead to: + +.vbcpToCell +|=== +| vbcp | External Cells + +|(1) | - +|(2) | - +|(3,4) | 1 External cells +|(3,5) | 3 External cells +|=== + +Therefore the algorithm works on structuring cells that are the ones that are not vertical. + +Among these structuring cells, the algorithm will look for candidate flat cells (reminder: a flat cell is a 3-node cell having the `BUS-SWITCH-BUS` pattern for which the two buses have the same vertical position, and have contiguous horizontal positions). + +Then it will chain the `BUS` nodes with the `candidateFlatCell` into `HorizontalChain` objects. + +`VerticalBusConnectionPattern` class:: +qsd +`VerticalBusConnectionPattern` class:: +qsd diff --git a/single-line-diagram-core/doc/Subsections.adoc b/single-line-diagram-core/doc/Subsections.adoc new file mode 100644 index 000000000..558d23f77 --- /dev/null +++ b/single-line-diagram-core/doc/Subsections.adoc @@ -0,0 +1,5 @@ +== Manages `Subsections` +Subsections detects and manages the case of none parallel busbar structures. +**** +image::images/subsections.svg[] +**** diff --git a/single-line-diagram-core/doc/images/CellType_INTERN.svg b/single-line-diagram-core/doc/images/CellType_INTERN.svg new file mode 100644 index 000000000..db3e02bd7 --- /dev/null +++ b/single-line-diagram-core/doc/images/CellType_INTERN.svg @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + busbars + subsections + + + + diff --git a/single-line-diagram-core/doc/images/CellTypes.svg b/single-line-diagram-core/doc/images/CellTypes.svg new file mode 100644 index 000000000..c1fb6271f --- /dev/null +++ b/single-line-diagram-core/doc/images/CellTypes.svg @@ -0,0 +1,590 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INTERN + + INTERN + + + + + + + + + + + + + INTERNBOUND + + EXTERN + + EXTERN + + + + + + + + + + + + + EXTERN + + + + + + + + + + + + + EXTERN + + + + + + + SHUNT + + diff --git a/single-line-diagram-core/doc/images/Cells.svg b/single-line-diagram-core/doc/images/Cells.svg new file mode 100644 index 000000000..69a1597f2 --- /dev/null +++ b/single-line-diagram-core/doc/images/Cells.svg @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + busbars + subsections + + + + diff --git a/single-line-diagram-core/doc/images/PowsyblLayout_HPos.svg b/single-line-diagram-core/doc/images/PowsyblLayout_HPos.svg new file mode 100644 index 000000000..a7e8d0e27 --- /dev/null +++ b/single-line-diagram-core/doc/images/PowsyblLayout_HPos.svg @@ -0,0 +1,3199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Stack + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + V + V + V + V + V + V + V + V + V + V + V + V + V + + V + V + V + V + V + V + V + V + H + H + H + H + H + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + V + V + V + H + H + + + + AbsoluteH Position + + + V->H=> + + BlockChain + + BlockParallel + + + + V->H=> + + + 1/2 Δx + + + + 1/2 Δx + + + + 1/2 Δx + + + + 1/2 Δx + + + + 1/2 Δx + + + + 1/2 Δx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/single-line-diagram-core/doc/images/busbars.svg b/single-line-diagram-core/doc/images/busbars.svg new file mode 100644 index 000000000..b352078c3 --- /dev/null +++ b/single-line-diagram-core/doc/images/busbars.svg @@ -0,0 +1,578 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (1,1) + (2,1) + + (1,2) + (2,2) + (1,3) + + + 1 + + + + 2 + + + + 3 + + + + 4 + + diff --git a/single-line-diagram-core/doc/images/rawGraph.svg b/single-line-diagram-core/doc/images/rawGraph.svg new file mode 100644 index 000000000..d0cc273ef --- /dev/null +++ b/single-line-diagram-core/doc/images/rawGraph.svg @@ -0,0 +1,748 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/single-line-diagram-core/doc/images/rawGraph.svg.2019_04_01_16_43_32.0.svg b/single-line-diagram-core/doc/images/rawGraph.svg.2019_04_01_16_43_32.0.svg new file mode 100644 index 000000000..5bf92d807 --- /dev/null +++ b/single-line-diagram-core/doc/images/rawGraph.svg.2019_04_01_16_43_32.0.svg @@ -0,0 +1,865 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/single-line-diagram-core/doc/images/rawGraphExtern.svg b/single-line-diagram-core/doc/images/rawGraphExtern.svg new file mode 100644 index 000000000..e5175e657 --- /dev/null +++ b/single-line-diagram-core/doc/images/rawGraphExtern.svg @@ -0,0 +1,835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bottlenecknodes + + + + + + OR + diff --git a/single-line-diagram-core/doc/images/rawGraphExternShunt.svg b/single-line-diagram-core/doc/images/rawGraphExternShunt.svg new file mode 100644 index 000000000..5f2f61a2a --- /dev/null +++ b/single-line-diagram-core/doc/images/rawGraphExternShunt.svg @@ -0,0 +1,768 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SHUNT + diff --git a/single-line-diagram-core/doc/images/rawGraphIntern.svg b/single-line-diagram-core/doc/images/rawGraphIntern.svg new file mode 100644 index 000000000..4c8fccee6 --- /dev/null +++ b/single-line-diagram-core/doc/images/rawGraphIntern.svg @@ -0,0 +1,794 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/single-line-diagram-core/doc/images/rawPosition.svg b/single-line-diagram-core/doc/images/rawPosition.svg new file mode 100644 index 000000000..a94e75fcd --- /dev/null +++ b/single-line-diagram-core/doc/images/rawPosition.svg @@ -0,0 +1,580 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + (1,1) + (2,1) + (1,2) + (2,2) + (1,3) + + + 1 + + + + 2 + + + + 3 + + + + 4 + + + + diff --git a/single-line-diagram-core/doc/images/subsections.svg b/single-line-diagram-core/doc/images/subsections.svg new file mode 100644 index 000000000..db3e02bd7 --- /dev/null +++ b/single-line-diagram-core/doc/images/subsections.svg @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + busbars + subsections + + + + diff --git a/single-line-diagram-core/doc/images/verticalBusConnectionPattern.svg b/single-line-diagram-core/doc/images/verticalBusConnectionPattern.svg new file mode 100644 index 000000000..f63a301fe --- /dev/null +++ b/single-line-diagram-core/doc/images/verticalBusConnectionPattern.svg @@ -0,0 +1,605 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + 2 + 3 + 4 + 5 + (1) + (2) + (1) + (3) + (4) + (5) + (3,5) + (3,4) + (3,5) + (3,5) + verticalBusConnectionPatterns + + + diff --git a/single-line-diagram-core/pom.xml b/single-line-diagram-core/pom.xml new file mode 100644 index 000000000..d60004976 --- /dev/null +++ b/single-line-diagram-core/pom.xml @@ -0,0 +1,88 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-single-line-diagram + 1.0.0-SNAPSHOT + + + powsybl-single-line-diagram-core + Single line diagram core + + + + + org.jgrapht + jgrapht-core + + + org.apache.xmlgraphics + batik-anim + + + org.apache.xmlgraphics + batik-bridge + + + + com.powsybl + powsybl-base-voltage-color + ${project.version} + + + com.powsybl + powsybl-single-line-diagram-iidm-extensions + ${project.version} + + + + com.powsybl + powsybl-iidm-api + + + com.powsybl + powsybl-iidm-converter-api + + + + + com.powsybl + powsybl-iidm-impl + runtime + + + com.powsybl + powsybl-iidm-xml-converter + runtime + + + + + junit + junit + test + + + org.slf4j + slf4j-simple + test + + + com.google.jimfs + jimfs + test + + + + diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/SubstationDiagram.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/SubstationDiagram.java new file mode 100644 index 000000000..f096101b7 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/SubstationDiagram.java @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Substation; +import com.powsybl.sld.layout.HorizontalSubstationLayoutFactory; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayoutFactory; +import com.powsybl.sld.layout.SubstationLayout; +import com.powsybl.sld.layout.SubstationLayoutFactory; +import com.powsybl.sld.layout.VoltageLevelLayoutFactory; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.model.SubstationGraph; +import com.powsybl.sld.svg.DefaultNodeLabelConfiguration; +import com.powsybl.sld.svg.DefaultSubstationDiagramInitialValueProvider; +import com.powsybl.sld.svg.DefaultSubstationDiagramStyleProvider; +import com.powsybl.sld.svg.GraphMetadata; +import com.powsybl.sld.svg.DefaultSVGWriter; +import com.powsybl.sld.svg.NodeLabelConfiguration; +import com.powsybl.sld.svg.SVGWriter; +import com.powsybl.sld.svg.SubstationDiagramInitialValueProvider; +import com.powsybl.sld.svg.SubstationDiagramStyleProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +/** + * @author Franck Lecuyer + */ +public final class SubstationDiagram { + + private static final Logger LOGGER = LoggerFactory.getLogger(SubstationDiagram.class); + + private final SubstationGraph subGraph; + + private final SubstationLayout subLayout; + + private SubstationDiagram(SubstationGraph graph, SubstationLayout layout) { + this.subGraph = Objects.requireNonNull(graph); + this.subLayout = Objects.requireNonNull(layout); + } + + public static SubstationDiagram build(Substation s) { + return build(s, new HorizontalSubstationLayoutFactory(), new PositionVoltageLevelLayoutFactory(), false); + } + + public static SubstationDiagram build(Substation s, SubstationLayoutFactory sLayoutFactory, + VoltageLevelLayoutFactory vLayoutFactory, boolean useName) { + Objects.requireNonNull(s); + Objects.requireNonNull(sLayoutFactory); + Objects.requireNonNull(vLayoutFactory); + + SubstationGraph graph = SubstationGraph.create(s, useName); + + SubstationLayout layout = sLayoutFactory.create(graph, vLayoutFactory); + + return new SubstationDiagram(graph, layout); + } + + public void writeSvg(String prefixId, ComponentLibrary componentLibrary, LayoutParameters layoutParameters, Network network, Path svgFile) { + SVGWriter writer = new DefaultSVGWriter(componentLibrary, layoutParameters); + writeSvg(prefixId, writer, svgFile, network); + } + + public void writeSvg(String prefixId, SVGWriter writer, Network network, Path svgFile) { + writeSvg(prefixId, writer, svgFile, network); + } + + public void writeSvg(String prefixId, SVGWriter writer, Path svgFile, Network network) { + Path dir = svgFile.toAbsolutePath().getParent(); + String svgFileName = svgFile.getFileName().toString(); + if (!svgFileName.endsWith(".svg")) { + svgFileName = svgFileName + ".svg"; + } + + try (Writer svgWriter = Files.newBufferedWriter(svgFile, StandardCharsets.UTF_8); + Writer metadataWriter = Files.newBufferedWriter(dir.resolve(svgFileName.replace(".svg", "_metadata.json")), StandardCharsets.UTF_8)) { + writeSvg(prefixId, writer, svgWriter, metadataWriter, network); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public void writeSvg(String prefixId, SVGWriter writer, Writer svgWriter, Writer metadataWriter, Network network) { + writeSvg(prefixId, writer, + new DefaultSubstationDiagramInitialValueProvider(network), + new DefaultSubstationDiagramStyleProvider(), + new DefaultNodeLabelConfiguration(writer.getComponentLibrary()), + svgWriter, + metadataWriter); + } + + public void writeSvg(String prefixId, + ComponentLibrary componentLibrary, LayoutParameters layoutParameters, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Writer svgWriter, Writer metadataWriter) { + SVGWriter writer = new DefaultSVGWriter(componentLibrary, layoutParameters); + writeSvg(prefixId, writer, initProvider, styleProvider, nodeLabelConfiguration, svgWriter, metadataWriter); + } + + public void writeSvg(String prefixId, + SVGWriter writer, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Writer svgWriter, Writer metadataWriter) { + Objects.requireNonNull(writer); + Objects.requireNonNull(writer.getLayoutParameters()); + Objects.requireNonNull(svgWriter); + Objects.requireNonNull(metadataWriter); + + subLayout.run(writer.getLayoutParameters()); + + // write SVG file + LOGGER.info("Writing SVG and JSON metadata files..."); + + GraphMetadata metadata = writer.write(prefixId, subGraph, initProvider, styleProvider, nodeLabelConfiguration, svgWriter); + + // write metadata file + metadata.writeJson(metadataWriter); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/VoltageLevelDiagram.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/VoltageLevelDiagram.java new file mode 100644 index 000000000..9699d6417 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/VoltageLevelDiagram.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.VoltageLevelLayout; +import com.powsybl.sld.layout.VoltageLevelLayoutFactory; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.svg.DefaultNodeLabelConfiguration; +import com.powsybl.sld.svg.DefaultSubstationDiagramInitialValueProvider; +import com.powsybl.sld.svg.DefaultSubstationDiagramStyleProvider; +import com.powsybl.sld.svg.GraphMetadata; +import com.powsybl.sld.svg.DefaultSVGWriter; +import com.powsybl.sld.svg.NodeLabelConfiguration; +import com.powsybl.sld.svg.SVGWriter; +import com.powsybl.sld.svg.SubstationDiagramInitialValueProvider; +import com.powsybl.sld.svg.SubstationDiagramStyleProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public final class VoltageLevelDiagram { + + private static final Logger LOGGER = LoggerFactory.getLogger(VoltageLevelDiagram.class); + + private final Graph graph; + + private final VoltageLevelLayout vlLayout; + + private VoltageLevelDiagram(Graph graph, VoltageLevelLayout layout) { + this.graph = Objects.requireNonNull(graph); + this.vlLayout = Objects.requireNonNull(layout); + } + + public static VoltageLevelDiagram build(VoltageLevel vl, VoltageLevelLayoutFactory layoutFactory, + boolean useName, boolean showInductorFor3WT) { + Objects.requireNonNull(vl); + Objects.requireNonNull(layoutFactory); + + Graph graph = Graph.create(vl, useName, true, showInductorFor3WT); + + VoltageLevelLayout layout = layoutFactory.create(graph); + + return new VoltageLevelDiagram(graph, layout); + } + + public void writeSvg(String prefixId, + ComponentLibrary componentLibrary, + LayoutParameters layoutParameters, + Network network, + Path svgFile) { + SVGWriter writer = new DefaultSVGWriter(componentLibrary, layoutParameters); + writeSvg(prefixId, writer, + new DefaultSubstationDiagramInitialValueProvider(network), + new DefaultSubstationDiagramStyleProvider(), + new DefaultNodeLabelConfiguration(writer.getComponentLibrary()), + svgFile, false); + } + + public void writeSvg(String prefixId, SVGWriter writer, Network network, Path svgFile) { + writeSvg(prefixId, writer, + new DefaultSubstationDiagramInitialValueProvider(network), + new DefaultSubstationDiagramStyleProvider(), + new DefaultNodeLabelConfiguration(writer.getComponentLibrary()), + svgFile, false); + } + + public void writeSvg(String prefixId, + SVGWriter writer, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Path svgFile, + boolean debug) { + Path dir = svgFile.toAbsolutePath().getParent(); + String svgFileName = svgFile.getFileName().toString(); + if (!svgFileName.endsWith(".svg")) { + svgFileName = svgFileName + ".svg"; + } + try (Writer svgWriter = Files.newBufferedWriter(svgFile, StandardCharsets.UTF_8); + Writer metadataWriter = Files.newBufferedWriter(dir.resolve(svgFileName.replace(".svg", "_metadata.json")), StandardCharsets.UTF_8)) { + writeSvg(prefixId, writer, initProvider, styleProvider, nodeLabelConfiguration, svgWriter, metadataWriter); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public void writeSvg(String prefixId, + ComponentLibrary componentLibrary, LayoutParameters layoutParameters, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Writer svgWriter, + Writer metadataWriter) { + SVGWriter writer = new DefaultSVGWriter(componentLibrary, layoutParameters); + writeSvg(prefixId, writer, initProvider, styleProvider, nodeLabelConfiguration, svgWriter, metadataWriter); + } + + public void writeSvg(String prefixId, + SVGWriter writer, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Writer svgWriter, + Writer metadataWriter) { + Objects.requireNonNull(writer); + Objects.requireNonNull(writer.getLayoutParameters()); + Objects.requireNonNull(svgWriter); + Objects.requireNonNull(metadataWriter); + + // calculate coordinate + vlLayout.run(writer.getLayoutParameters()); + + // write SVG file + LOGGER.info("Writing SVG and JSON metadata files..."); + + GraphMetadata metadata = writer.write(prefixId, graph, initProvider, styleProvider, nodeLabelConfiguration, svgWriter); + + // write metadata file + metadata.writeJson(metadataWriter); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/AbstractSubstationLayout.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/AbstractSubstationLayout.java new file mode 100644 index 000000000..e76f92f41 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/AbstractSubstationLayout.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.sld.model.BusCell; +import com.powsybl.sld.model.BusCell.Direction; +import com.powsybl.sld.model.Coord; +import com.powsybl.sld.model.Edge; +import com.powsybl.sld.model.ExternCell; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.Node; +import com.powsybl.sld.model.Side; +import com.powsybl.sld.model.SubstationGraph; +import com.powsybl.sld.model.TwtEdge; + +/** + * @author Franck Lecuyer + * @author Massimo Ferraro + */ +public abstract class AbstractSubstationLayout implements SubstationLayout { + + protected SubstationGraph graph; + protected VoltageLevelLayoutFactory vLayoutFactory; + + public AbstractSubstationLayout(SubstationGraph graph, VoltageLevelLayoutFactory vLayoutFactory) { + this.graph = Objects.requireNonNull(graph); + this.vLayoutFactory = Objects.requireNonNull(vLayoutFactory); + } + + @Override + public void run(LayoutParameters layoutParameters) { + double graphX = layoutParameters.getHorizontalSubstationPadding(); + double graphY = layoutParameters.getVerticalSubstationPadding(); + + for (Graph vlGraph : graph.getNodes()) { + vlGraph.setX(graphX); + vlGraph.setY(graphY); + + // Calculate the objects coordinates inside the voltageLevel graph + VoltageLevelLayout vLayout = vLayoutFactory.create(vlGraph); + vLayout.run(layoutParameters); + + // Calculate the coordinate of the voltageLevel graph inside the substation graph + Coord posVLGraph = calculateCoordVoltageLevel(layoutParameters, vlGraph); + + graphX += posVLGraph.getX() + getHorizontalSubstationPadding(layoutParameters); + graphY += posVLGraph.getY() + getVerticalSubstationPadding(layoutParameters); + } + + // Determine points of the snakeLine + Map nbSnakeLinesTopBottom = EnumSet.allOf(BusCell.Direction.class).stream().collect(Collectors.toMap(Function.identity(), v -> 0)); + Map nbSnakeLinesBetween = graph.getNodes().stream().collect(Collectors.toMap(g -> g.getVoltageLevel().getId(), v -> 0)); + Map nbSnakeLinesLeftRight = EnumSet.allOf(Side.class).stream().collect(Collectors.toMap(Function.identity(), v -> 0)); + Map nbSnakeLinesBottomVL = graph.getNodes().stream().collect(Collectors.toMap(g -> g.getVoltageLevel().getId(), v -> 0)); + Map nbSnakeLinesTopVL = graph.getNodes().stream().collect(Collectors.toMap(g -> g.getVoltageLevel().getId(), v -> 0)); + + for (TwtEdge edge : graph.getEdges()) { + List pol = calculatePolylineSnakeLine(layoutParameters, + edge, + nbSnakeLinesTopBottom, + nbSnakeLinesLeftRight, + nbSnakeLinesBetween, + nbSnakeLinesBottomVL, + nbSnakeLinesTopVL); + edge.setSnakeLine(pol); + } + } + + protected abstract Coord calculateCoordVoltageLevel(LayoutParameters layoutParameters, Graph vlGraph); + + protected abstract double getHorizontalSubstationPadding(LayoutParameters layoutParameters); + + protected abstract double getVerticalSubstationPadding(LayoutParameters layoutParameters); + + protected abstract List calculatePolylineSnakeLine(LayoutParameters layoutParameters, Edge edge, + Map nbSnakeLinesTopBottom, Map nbSnakeLinesLeftRight, + Map nbSnakeLinesBetween, Map nbSnakeLinesBottomVL, + Map nbSnakeLinesTopVL); + + protected BusCell.Direction getNodeDirection(Node node, int nb) { + if (node.getType() != Node.NodeType.FEEDER) { + throw new PowsyblException("Node " + nb + " is not a feeder node"); + } + BusCell.Direction dNode = node.getCell() != null ? ((ExternCell) node.getCell()).getDirection() : BusCell.Direction.TOP; + if (dNode != BusCell.Direction.TOP && dNode != BusCell.Direction.BOTTOM) { + throw new PowsyblException("Node " + nb + " cell direction not TOP or BOTTOM"); + } + return dNode; + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockOrganizer.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockOrganizer.java new file mode 100644 index 000000000..4707cd1e0 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockOrganizer.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class BlockOrganizer { + + private static final Logger LOGGER = LoggerFactory.getLogger(BlockOrganizer.class); + + private final PositionFinder positionFinder; + + private final boolean stack; + + public BlockOrganizer() { + this(new PositionFromExtension(), true); + } + + public BlockOrganizer(boolean stack) { + this(new PositionFree(), stack); + } + + public BlockOrganizer(PositionFinder positionFinder) { + this(positionFinder, true); + } + + public BlockOrganizer(PositionFinder positionFinder, boolean stack) { + this.positionFinder = Objects.requireNonNull(positionFinder); + this.stack = stack; + } + + /** + * Organize cells into blocks and call the layout resolvers + */ + public void organize(Graph graph) { + LOGGER.info("Organizing graph cells into blocks"); + graph.getCells().stream() + .filter(cell -> cell.getType().equals(Cell.CellType.EXTERN) + || cell.getType().equals(Cell.CellType.INTERN)) + .forEach(cell -> { + new CellBlockDecomposer().determineBlocks(cell); + if (cell.getType() == Cell.CellType.INTERN) { + ((InternCell) cell).organizeBlocks(); + } + }); + graph.getCells().stream() + .filter(cell -> cell.getType() == Cell.CellType.SHUNT) + .forEach(cell -> new CellBlockDecomposer().determineBlocks(cell)); + + if (stack) { + determineStackableBlocks(graph); + } + positionFinder.buildLayout(graph); + + graph.getCells().stream() + .filter(c -> c.getType() == Cell.CellType.INTERN) + .forEach(c -> ((InternCell) c).postPositioningSettings()); + + SubSections subSections = new SubSections(graph); + subSections.handleSpanningBusBar(); + LOGGER.debug("Subsections {}", subSections); + + graph.getCells().stream() + .filter(cell -> cell instanceof BusCell) + .forEach(cell -> ((BusCell) cell).blockSizing()); + new BlockPositionner().determineBlockPositions(graph, subSections); + } + + /** + * Determines blocks connected to busbar that are stackable + */ + private void determineStackableBlocks(Graph graph) { + LOGGER.info("Determining stackable Blocks"); + graph.getBusCells().forEach(cell -> { + List blocks = cell.getPrimaryLegBlocks(); + for (int i = 0; i < blocks.size(); i++) { + LegPrimaryBlock block1 = blocks.get(i); + if (block1.getNodes().size() == 3) { + for (int j = i + 1; j < blocks.size(); j++) { + LegPrimaryBlock block2 = blocks.get(j); + if (block2.getNodes().size() == 3 + && block1.getExtremityNode(Block.Extremity.END).equals(block2.getExtremityNode(Block.Extremity.END)) + && !block1.getExtremityNode(Block.Extremity.START).equals(block2.getExtremityNode(Block.Extremity.START))) { + block1.addStackableBlock(block2); + block2.addStackableBlock(block1); + } + } + } + } + }); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockPositionner.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockPositionner.java new file mode 100644 index 000000000..d6e072fdc --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/BlockPositionner.java @@ -0,0 +1,223 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + */ +public class BlockPositionner { + + void determineBlockPositions(Graph graph, SubSections subSections) { + int hPos = 0; + int prevHPos = 0; + int hSpace = 0; + int maxV = graph.getMaxBusStructuralPosition().getV(); + List nonFlatCellsToClose = new ArrayList<>(); + + int[] previousIndexes = new int[maxV]; + graph.getNodeBuses().forEach(nodeBus -> nodeBus.getPosition().setV(nodeBus.getStructuralPosition().getV())); + + for (Map.Entry entry : + subSections.getSubsectionMap().entrySet()) { + int[] ssIndexes = entry.getKey().getIndexes(); + SubSections.HorizontalSubSection hSs = entry.getValue(); + for (int vPos = 0; vPos < maxV; vPos++) { + if (ssIndexes[vPos] != previousIndexes[vPos]) { + updateNodeBusPos(graph, vPos, hPos, hSpace, previousIndexes, Side.LEFT); + updateNodeBusPos(graph, vPos, hPos, 0, ssIndexes, Side.RIGHT); + } + } + hPos = placeNonFlatInternCells(hPos, hSs, Side.LEFT, nonFlatCellsToClose); + hPos = placeVerticalCells(hPos, new ArrayList<>(hSs.getSideInternCells(Side.UNDEFINED))); + hPos = placeVerticalCells(hPos, hSs.getExternCells().stream() + .sorted(Comparator.comparingInt(ExternCell::getOrder)) + .collect(Collectors.toList())); + hPos = placeNonFlatInternCells(hPos, hSs, Side.RIGHT, nonFlatCellsToClose); + if (hPos == prevHPos) { + hPos++; + } + hSpace = placeFlatInternCells(hPos, hSs.getSideInternCells(Side.RIGHT).stream() + .filter(InternCell::isFlat) + .collect(Collectors.toList())) - hPos; + hPos += hSpace; + prevHPos = hPos; + previousIndexes = ssIndexes; + } + for (int vPos = 0; vPos < maxV; vPos++) { + updateNodeBusPos(graph, vPos, hPos, hSpace, previousIndexes, Side.LEFT); + } + manageInternCellOverlaps(graph); + } + + private void updateNodeBusPos(Graph graph, int vPos, int hPos, int hSpace, int[] indexes, Side side) { + if (indexes[vPos] != 0) { + Position p = graph.getVHNodeBus(vPos + 1, indexes[vPos]).getPosition(); + if (side == Side.LEFT) { + p.setHSpan(hPos - Math.max(p.getH(), 0) - hSpace); + } else if (side == Side.RIGHT && (p.getH() == -1 || hPos == 0)) { + p.setH(hPos); + } + } + } + + private int placeVerticalCells(int hPos, Collection busCells) { + int hPosRes = hPos; + for (BusCell cell : busCells) { + hPosRes = cell.newHPosition(hPosRes); + } + return hPosRes; + } + + private int placeFlatInternCells(int hPos, List cells) { + int hPosRes = hPos; + for (BusCell cell : cells) { + hPosRes = Math.max(hPosRes, cell.newHPosition(hPos)); + } + return hPosRes; + } + + private int placeNonFlatInternCells(int hPos, + SubSections.HorizontalSubSection hSs, + Side side, List nonFlatCellsToClose) { + int hPosRes = hPos; + List cells = hSs.getSideInternCells(side).stream() + .filter(internCell -> !internCell.isFlat()) + .sorted(Comparator.comparingInt(c -> -nonFlatCellsToClose.indexOf(c))) + .collect(Collectors.toList()); + Side legSide = side.getFlip(); + for (InternCell cell : cells) { + hPosRes = cell.newHPosition(hPosRes, legSide); + } + if (side == Side.RIGHT) { + nonFlatCellsToClose.addAll(cells); + } else { + nonFlatCellsToClose.removeAll(cells); + } + return hPosRes; + } + + private void manageInternCellOverlaps(Graph graph) { + List cellsToHandle = graph.getCells().stream() + .filter(cell -> cell.getType() == Cell.CellType.INTERN) + .map(InternCell.class::cast) + .filter(internCell -> internCell.getDirection() != BusCell.Direction.FLAT + && internCell.getBodyBlock() != null) + .collect(Collectors.toList()); + Lane lane = new Lane(cellsToHandle); + lane.run(); + } + + /** + * The class lane manages the overlaps of internCells. + * After bundleToCompatibleLanes each lane contents non overlapping cells + * arrangeLane at this stage balance the lanes on TOP and BOTTOM this could be improved by having various VPos per lane + */ + private class Lane { + HashMap> incompatibilities; + Lane nextLane; + List lanes; + + Lane(List cells) { + lanes = new ArrayList<>(); + lanes.add(this); + incompatibilities = new HashMap<>(); + cells.forEach(this::addCell); + } + + Lane(InternCell cell, List lanes) { + this.lanes = lanes; + lanes.add(this); + incompatibilities = new HashMap<>(); + addCell(cell); + } + + void run() { + bundleToCompatibleLanes(); + arrangeLanes(); + } + + private void addCell(InternCell cell) { + incompatibilities.put(cell, new ArrayList<>()); + } + + private void bundleToCompatibleLanes() { + while (identifyIncompatibilities()) { + shiftIncompatibilities(); + } + } + + private boolean identifyIncompatibilities() { + boolean hasIncompatibility = false; + for (Map.Entry> entry : incompatibilities.entrySet()) { + InternCell internCellA = entry.getKey(); + entry.getValue().clear(); + int hAmin = internCellA.getSideHPos(Side.LEFT); + int hAmax = internCellA.getSideHPos(Side.RIGHT); + + for (InternCell internCellB : incompatibilities.keySet()) { + if (!internCellA.equals(internCellB)) { + int hBmin = internCellB.getSideHPos(Side.LEFT); + int hBmax = internCellB.getSideHPos(Side.RIGHT); + if (hAmax > hBmin && hBmax > hAmin) { + entry.getValue().add(internCellB); + hasIncompatibility = true; + } + } + } + } + return hasIncompatibility; + } + + private void shiftIncompatibilities() { + Map.Entry> entry = incompatibilities.entrySet().stream() + .filter(e -> !e.getValue().isEmpty()) + .max(Comparator.comparingInt(e -> e.getValue().size())).orElse(null); + + if (entry != null) { + InternCell cell = entry.getKey(); + incompatibilities.remove(cell); + if (nextLane == null) { + nextLane = new Lane(cell, lanes); + } else { + nextLane.addCell(cell); + nextLane.bundleToCompatibleLanes(); + } + } + } + + private void arrangeLanes() { + int i = 0; + for (Lane lane : lanes) { + final int j = i % 2; + final int newV = 1 + i / 2; + lane.incompatibilities.keySet() + .forEach(c -> { + c.setDirection(j == 0 ? BusCell.Direction.TOP : BusCell.Direction.BOTTOM); + c.getBodyBlock().getPosition().setV(newV); + }); + i++; + } + } + + public String toString() { + StringBuilder str = new StringBuilder(incompatibilities.toString() + "\n\n"); + if (nextLane != null) { + str.append(nextLane.toString()); + } + return new String(str); + } + + public int size() { + return incompatibilities.size(); + } + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/CellBlockDecomposer.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/CellBlockDecomposer.java new file mode 100644 index 000000000..668a533a6 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/CellBlockDecomposer.java @@ -0,0 +1,254 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Contain function to dispose components of cells based on Hierarchical Layout + * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class CellBlockDecomposer { + + private static final Logger LOGGER = LoggerFactory.getLogger(CellBlockDecomposer.class); + + /** + * Search BlockPrimary and build Block hierarchy by merging blocks together; also + * list blocks connected to busbar + * + * @param cell Cell we are working on + */ + public void determineBlocks(Cell cell) { + if (cell.getType() == Cell.CellType.SHUNT) { + determineShuntCellBlocks((ShuntCell) cell); + } else { + determineBusCellBlocks((BusCell) cell); + } + } + + private void determineBusCellBlocks(BusCell busCell) { + if (busCell.getType() == Cell.CellType.INTERN && busCell.getNodes().size() == 3) { + SwitchNode switchNode = (SwitchNode) busCell.getNodes().get(1); + busCell.getGraph().extendSwitchBetweenBus(switchNode); + List adj = switchNode.getAdjacentNodes(); + busCell.addNodes(adj); + busCell.addNodes(adj.stream() + .flatMap(node -> node.getAdjacentNodes().stream()) + .filter(node -> node != switchNode) + .collect(Collectors.toList())); + } + determineComplexCell(busCell); + } + + private void determineShuntCellBlocks(ShuntCell shuntCell) { + BodyPrimaryBlock bpy = new BodyPrimaryBlock(shuntCell.getNodes(), shuntCell); + shuntCell.setRootBlock(bpy); + } + + private void determineComplexCell(BusCell busCell) { + List blocks = createPrimaryBlock(busCell); + mergeBlocks(busCell, blocks); + } + + private List createPrimaryBlock(BusCell busCell) { + List alreadyTreated = new ArrayList<>(); + List blocks = new ArrayList<>(); + Node currentNode = busCell.getBusNodes().get(0); + + // Search all primary blocks + currentNode.getListNodeAdjInCell(busCell).forEach(n -> { + if (!alreadyTreated.contains(n)) { + List blockNodes = new ArrayList<>(); + blockNodes.add(currentNode); + rElaboratePrimaryBlocks(busCell, n, currentNode, alreadyTreated, blockNodes, blocks); + } + }); + return blocks; + } + + private void mergeBlocks(BusCell busCell, List blocks) { + // Search all blocks connected to a busbar inside the primary blocks list + List primaryLegBlocks = blocks.stream() + .filter(b -> b instanceof LegPrimaryBlock) + .map(LegPrimaryBlock.class::cast) + .collect(Collectors.toList()); + + // Merge blocks to obtain a hierarchy of blocks + while (blocks.size() != 1) { + boolean merged = searchParallelMerge(blocks, busCell); + merged |= searchSerialMerge(blocks, busCell); + if (!merged) { + LOGGER.warn("{} busCell, cannot merge any additional blocks, {} blocks remains", busCell.getType(), blocks.size()); + Block undefinedBlock = new UndefinedBlock(new ArrayList<>(blocks), busCell); + blocks.clear(); + blocks.add(undefinedBlock); + break; + } + } + busCell.blocksSetting(blocks.get(0), primaryLegBlocks); + } + + /** + * Search possibility to merge two blocks into a chain layout.block and do the merging + * + * @param blocks list of blocks we can merge + * @param cell current cell + */ + private boolean searchSerialMerge(List blocks, Cell cell) { + int i = 0; + boolean identifiedMerge = false; + + while (i < blocks.size()) { + List blockToRemove = new ArrayList<>(); + boolean chainIdentified = false; + Block b1 = blocks.get(i); + SerialBlock serialBlock = new SerialBlock(b1, cell); + for (int j = i + 1; j < blocks.size(); j++) { + Block b2 = blocks.get(j); + if (serialBlock.addSubBlock(b2)) { + chainIdentified = true; + blockToRemove.add(b2); + } + } + if (chainIdentified) { + blockToRemove.add(b1); + identifiedMerge = true; + blocks.removeAll(blockToRemove); + blocks.add(i, serialBlock); + } + i++; + } + return identifiedMerge; + } + + + /** + * Search possibility to merge some blocks into a parallel layout.block and do the merging + * + * @param blocks list of blocks we can merge + * @param cell current cell + */ + private boolean searchParallelMerge(List blocks, Cell cell) { + List> blocksBundlesToMerge = new ArrayList<>(); + Node commonNode; + int i = 0; + while (i < blocks.size()) { + List blocksBundle = new ArrayList<>(); + for (int j = i + 1; j < blocks.size(); j++) { + commonNode = checkParallelCriteria(blocks.get(i), blocks.get(j)); + if (commonNode != null) { + blocksBundle.add(blocks.get(j)); + } + } + if (blocksBundle.isEmpty()) { + i++; + } else { + blocksBundle.add(blocks.get(i)); + blocks.removeAll(blocksBundle); + blocksBundlesToMerge.add(blocksBundle); + } + } + for (List blocksBundle : blocksBundlesToMerge) { + ParallelBlock bPar; + if (blocksBundle.stream().anyMatch(b -> !(b instanceof LegPrimaryBlock))) { + bPar = new BodyParallelBlock(blocksBundle, cell, true); + } else { + bPar = new LegParralelBlock(blocksBundle, cell, true); + } + blocks.add(bPar); + } + return !blocksBundlesToMerge.isEmpty(); + } + + /** + * Compare two blocks to see if they are parallel + * + * @param block1 layout.block + * @param block2 layout.block + * @return true if the two blocks are similar : same start and end + */ + private Node checkParallelCriteria(Block block1, Block block2) { + Node s1 = block1.getExtremityNode(Block.Extremity.START); + Node e1 = block1.getExtremityNode(Block.Extremity.END); + Node s2 = block2.getExtremityNode(Block.Extremity.START); + Node e2 = block2.getExtremityNode(Block.Extremity.END); + + if ((s1.checkNodeSimilarity(s2) && e1.checkNodeSimilarity(e2)) + || (s1.checkNodeSimilarity(e2) && e1.checkNodeSimilarity(s2))) { + if (s1.equals(s2) || s1.equals(e2)) { + return s1; + } else { + return e1; + } + } else { + return null; + } + } + + /** + * Search for primaryBlock + * a primaryBlock is identified when the the following pattern is detected: + * BUS|FICTICIOUS|FEEDER|SHUNT - n * SWITCH - BUS|FICTICIOUS|FEEDER|SHUNT + * when there is one BUS, it is instanciated as PrimaryLegBlock with this pattern (only allowed pattern with a bus) : + * BUS - SWITCH - FICTICIOUS + * otherwise it is instanciated as PrimaryBodyBlock + * + * @param cell cell + * @param currentNode currentnode + * @param alreadyTreated alreadyTreated + * @param blockNodes blockNodes + * @param blocks blocks + */ + private void rElaboratePrimaryBlocks(Cell cell, Node currentNode, Node parentNode, + List alreadyTreated, + List blockNodes, + List blocks) { + Node currentNode2 = currentNode; + Node parentNode2 = parentNode; + + alreadyTreated.add(currentNode2); + blockNodes.add(currentNode2); + while (currentNode2.getType() == Node.NodeType.SWITCH) { + Node nextNode = currentNode2.getAdjacentNodes().get( + currentNode2.getAdjacentNodes().get(0).equals(parentNode2) ? 1 : 0); + parentNode2 = currentNode2; + currentNode2 = nextNode; + if (currentNode2.getType() != Node.NodeType.BUS) { + alreadyTreated.add(currentNode2); + } + blockNodes.add(currentNode2); + } + PrimaryBlock b; + if (blockNodes.stream().anyMatch(node -> node.getType() == Node.NodeType.BUS)) { + b = new LegPrimaryBlock(blockNodes, cell); + } else { + b = new BodyPrimaryBlock(blockNodes, cell); + } + blocks.add(b); + // If we did'nt reach a Busbar, continue to search for other + // blocks + if (currentNode2.getType() != Node.NodeType.BUS) { + Node finalCurrentNode = currentNode2; + currentNode2.getListNodeAdjInCell(cell) + .filter(node -> !alreadyTreated.contains(node) && !blockNodes.contains(node)) + .forEach(node -> { + List blockNode = new ArrayList<>(); + blockNode.add(finalCurrentNode); + rElaboratePrimaryBlocks(cell, node, finalCurrentNode, alreadyTreated, blockNode, blocks); + }); + } + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/CellDetector.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/CellDetector.java new file mode 100644 index 000000000..293a44570 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/CellDetector.java @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.Graph; + +/** + * this interface is implemented by classes determining the cells. + * The expected result of CellDector is the creation and association of cell + * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface CellDetector { + + void detectCells(Graph graph); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalSubstationLayout.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalSubstationLayout.java new file mode 100644 index 000000000..07b3c619e --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalSubstationLayout.java @@ -0,0 +1,174 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * @author Franck Lecuyer + */ +public class HorizontalSubstationLayout extends AbstractSubstationLayout { + + public HorizontalSubstationLayout(SubstationGraph graph, VoltageLevelLayoutFactory vLayoutFactory) { + super(graph, vLayoutFactory); + } + + /** + * Calculate relative coordinate of voltageLevel in the substation + */ + @Override + protected Coord calculateCoordVoltageLevel(LayoutParameters layoutParam, Graph vlGraph) { + int maxH = vlGraph.getNodeBuses().stream() + .mapToInt(nodeBus -> nodeBus.getPosition().getH() + nodeBus.getPosition().getHSpan()) + .max().orElse(0); + + double x = layoutParam.getInitialXBus() + (maxH + 2) * layoutParam.getCellWidth(); + double y = 0; + return new Coord(x, y); + } + + /* + * Calculate polyline points of a snakeLine in the substation graph + */ + @Override + protected List calculatePolylineSnakeLine(LayoutParameters layoutParam, + Edge edge, + Map nbSnakeLinesTopBottom, + Map nbSnakeLinesLeftRight, + Map nbSnakeLinesBetween, + Map nbSnakeLinesBottomVL, + Map nbSnakeLinesTopVL) { + Node node1 = edge.getNode1(); + Node node2 = edge.getNode2(); + + BusCell.Direction dNode1 = getNodeDirection(node1, 1); + BusCell.Direction dNode2 = getNodeDirection(node2, 2); + + double xMaxGraph; + String idMaxGraph; + + if (node1.getGraph().getX() > node2.getGraph().getX()) { + xMaxGraph = node1.getGraph().getX(); + idMaxGraph = node1.getGraph().getVoltageLevel().getId(); + } else { + xMaxGraph = node2.getGraph().getX(); + idMaxGraph = node2.getGraph().getVoltageLevel().getId(); + } + + double x1 = node1.getX(); + double y1 = node1.getY(); + double x2 = node2.getX(); + double y2 = node2.getY(); + + InfoCalcPoints info = new InfoCalcPoints(); + info.setLayoutParam(layoutParam); + info.setdNode1(dNode1); + info.setdNode2(dNode2); + info.setNbSnakeLinesTopBottom(nbSnakeLinesTopBottom); + info.setNbSnakeLinesBetween(nbSnakeLinesBetween); + info.setX1(x1); + info.setX2(x2); + info.setY1(y1); + info.setY2(y2); + info.setxMaxGraph(xMaxGraph); + info.setIdMaxGraph(idMaxGraph); + + return calculatePolylinePoints(info); + } + + public static List calculatePolylinePoints(InfoCalcPoints info) { + List pol = new ArrayList<>(); + + LayoutParameters layoutParam = info.getLayoutParam(); + BusCell.Direction dNode1 = info.getdNode1(); + BusCell.Direction dNode2 = info.getdNode2(); + Map nbSnakeLinesTopBottom = info.getNbSnakeLinesTopBottom(); + Map nbSnakeLinesBetween = info.getNbSnakeLinesBetween(); + double x1 = info.getX1(); + double x2 = info.getX2(); + double y1 = info.getY1(); + double y2 = info.getY2(); + double xMaxGraph = info.getxMaxGraph(); + String idMaxGraph = info.getIdMaxGraph(); + + switch (dNode1) { + case BOTTOM: + if (dNode2 == BusCell.Direction.BOTTOM) { // BOTTOM to BOTTOM + nbSnakeLinesTopBottom.compute(dNode1, (k, v) -> v + 1); + double decalV = nbSnakeLinesTopBottom.get(dNode1) * layoutParam.getVerticalSnakeLinePadding(); + double yDecal = Math.max(y1 + decalV, y2 + decalV); + + pol.addAll(Arrays.asList(x1, y1, + x1, yDecal, + x2, yDecal, + x2, y2)); + + } else { // BOTTOM to TOP + nbSnakeLinesTopBottom.compute(dNode1, (k, v) -> v + 1); + nbSnakeLinesTopBottom.compute(dNode2, (k, v) -> v + 1); + nbSnakeLinesBetween.compute(idMaxGraph, (k, v) -> v + 1); + double decal1V = nbSnakeLinesTopBottom.get(dNode1) * layoutParam.getVerticalSnakeLinePadding(); + double decal2V = nbSnakeLinesTopBottom.get(dNode2) * layoutParam.getVerticalSnakeLinePadding(); + double xBetweenGraph = xMaxGraph - (nbSnakeLinesBetween.get(idMaxGraph) * layoutParam.getHorizontalSnakeLinePadding()); + + pol.addAll(Arrays.asList(x1, y1, + x1, y1 + decal1V, + xBetweenGraph, y1 + decal1V, + xBetweenGraph, y2 - decal2V, + x2, y2 - decal2V, + x2, y2)); + } + break; + + case TOP: + if (dNode2 == BusCell.Direction.TOP) { // TOP to TOP + nbSnakeLinesTopBottom.compute(dNode1, (k, v) -> v + 1); + double decalV = nbSnakeLinesTopBottom.get(dNode1) * layoutParam.getVerticalSnakeLinePadding(); + double yDecal = Math.min(y1 - decalV, y2 - decalV); + + pol.addAll(Arrays.asList(x1, y1, + x1, yDecal, + x2, yDecal, + x2, y2)); + } else { // TOP to BOTTOM + nbSnakeLinesTopBottom.compute(dNode1, (k, v) -> v + 1); + nbSnakeLinesTopBottom.compute(dNode2, (k, v) -> v + 1); + nbSnakeLinesBetween.compute(idMaxGraph, (k, v) -> v + 1); + double decal1V = nbSnakeLinesTopBottom.get(dNode1) * layoutParam.getVerticalSnakeLinePadding(); + double decal2V = nbSnakeLinesTopBottom.get(dNode2) * layoutParam.getVerticalSnakeLinePadding(); + + double xBetweenGraph = xMaxGraph - (nbSnakeLinesBetween.get(idMaxGraph) * layoutParam.getHorizontalSnakeLinePadding()); + + pol.addAll(Arrays.asList(x1, y1, + x1, y1 - decal1V, + xBetweenGraph, y1 - decal1V, + xBetweenGraph, y2 + decal2V, + x2, y2 + decal2V, + x2, y2)); + } + break; + default: + } + return pol; + } + + @Override + protected double getHorizontalSubstationPadding(LayoutParameters layoutParameters) { + return layoutParameters.getHorizontalSubstationPadding(); + } + + @Override + protected double getVerticalSubstationPadding(LayoutParameters layoutParameters) { + return 0; + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalSubstationLayoutFactory.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalSubstationLayoutFactory.java new file mode 100644 index 000000000..30e784d88 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/HorizontalSubstationLayoutFactory.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.SubstationGraph; + +/** + * @author Franck Lecuyer + */ +public class HorizontalSubstationLayoutFactory implements SubstationLayoutFactory { + + @Override + public SubstationLayout create(SubstationGraph graph, VoltageLevelLayoutFactory vLayoutFactory) { + return new HorizontalSubstationLayout(graph, vLayoutFactory); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/ImplicitCellDetector.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/ImplicitCellDetector.java new file mode 100644 index 000000000..cc9a7c451 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/ImplicitCellDetector.java @@ -0,0 +1,357 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.Cell; +import com.powsybl.sld.model.ExternCell; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.InternCell; +import com.powsybl.sld.model.Node; +import com.powsybl.sld.model.ShuntCell; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class ImplicitCellDetector implements CellDetector { + + private static final Logger LOGGER = LoggerFactory.getLogger(ImplicitCellDetector.class); + private boolean removeUnnecessaryFictitiousNodes; + private boolean substituteSingularFictitiousByFeederNode; + + public ImplicitCellDetector(boolean removeUnnecessaryFictitiousNodes, boolean substituteSingularFictitiousByFeederNode) { + this.removeUnnecessaryFictitiousNodes = removeUnnecessaryFictitiousNodes; + this.substituteSingularFictitiousByFeederNode = substituteSingularFictitiousByFeederNode; + } + + public ImplicitCellDetector() { + this(true, true); + } + + + /** + * internCell detection : an internal cell is composed of nodes connecting BUSes without connecting Feeder. + * genericDetectCell is used to detect cells exploring the graph and scanning exclusionTypes and stopTypes + *

+ * *************INTERN CELL******************* + * exclusion types = {FEEDER} : if a FEEDER type is reached it is not an INTERN CELL + * stop the visit if reach a Bus : stopTypes = {BUS} + * ***************EXTERN AND SHUNT CELLS****** + * detection : nodes connecting buses and departures without being in an internCell (previously allocated nodes) + * exclusion types = {} + * stop the visit if reach a Bus : stopTypes = {BUS,FEEDER} * @param graph g + */ + @Override + public void detectCells(Graph graph) { + cleaning(graph); + + LOGGER.info("Detecting cells..."); + + List allocatedNodes = new ArrayList<>(); + // **************INTERN CELL******************* + List exclusionTypes = new ArrayList<>(); + exclusionTypes.add(Node.NodeType.FEEDER); + List stopTypes = new ArrayList<>(); + stopTypes.add(Node.NodeType.BUS); + genericDetectCell(graph, stopTypes, exclusionTypes, true, allocatedNodes); + + // ****************EXTERN AND SHUNT CELLS****** + stopTypes.add(Node.NodeType.FEEDER); + genericDetectCell(graph, stopTypes, new ArrayList<>(), false, allocatedNodes); + for (ExternCell cell : graph.getCells().stream() + + .filter(cell -> cell instanceof ExternCell) + .map(ExternCell.class::cast) + .collect(Collectors.toList())) { + + //*****************EXTERN CELL + if (!isPureExternCell(graph, cell)) { + //*****************SHUNT CELL + //in that case the cell is splitted into 2 EXTERN Cells and 1 SHUNT CELL + detectAndTypeShunt(graph, cell); + } + } + graph.getCells().forEach(Cell::getFullId); + + graph.logCellDetectionStatus(); + } + + private void cleaning(Graph graph) { + graph.substituteFictitiousNodesMirroringBusNodes(); + if (removeUnnecessaryFictitiousNodes) { + graph.removeUnnecessaryFictitiousNodes(); + } + graph.extendFeederWithMultipleSwitches(); + if (substituteSingularFictitiousByFeederNode) { + graph.substituteSingularFictitiousByFeederNode(); + } + graph.extendFirstOutsideNode(); + graph.extendBreakerConnectedToBus(); + graph.extendFeederConnectedToBus(); + } + + /** + * @param typeStops is the types of node that stops the exploration + * @param exclusionTypes is the types when reached considers the exploration unsuccessful + * @param isCellIntern when the exploration is for the identification of internCell enables to instanciate InternCell class instead of Cell + * @param allocatedNodes is the list of nodes already allocated to a cell. + **/ + private void genericDetectCell(Graph graph, + List typeStops, + List exclusionTypes, + boolean isCellIntern, + List allocatedNodes) { + graph.getNodeBuses().forEach(bus -> bus.getAdjacentNodes().forEach(adj -> { + List cellNodes = new ArrayList<>(); + List visitedNodes = new ArrayList<>(allocatedNodes); + visitedNodes.add(bus); + boolean searchOK = rDelimitedExploration(adj, typeStops, exclusionTypes, cellNodes, visitedNodes); + if (searchOK && !cellNodes.isEmpty()) { + cellNodes.add(adj); + cellNodes.add(bus); + Cell cell = isCellIntern ? new InternCell(graph) : new ExternCell(graph); + cell.setNodes(cellNodes); + allocatedNodes.addAll(cellNodes); + // remove the BusNodes from allocatedNode for a BusNode can be part of many cells + allocatedNodes.removeAll( + cellNodes.stream() + .filter(node -> node.getType() == Node.NodeType.BUS) + .collect(Collectors.toList())); + } + })); + } + + /** + * @param node the starting point for the exploration + * @param typeStops is the types of node that stops the exploration + * @param exclusionTypes is the types when reached considers the exploration unsuccessful + * @param nodesResult the resulting list of nodes + * @param exploredNodes nodes already visited + * @return true if no exclusionType found + **/ + private boolean rDelimitedExploration(Node node, + List typeStops, + List exclusionTypes, + List nodesResult, + List exploredNodes) { + + if (exploredNodes.contains(node)) { + return true; + } + exploredNodes.add(node); + // the node match the pattern if all the branches from its adjacent nodes reaches a typeStop node without reaching an exclusionTypes node + List nodesToVisit = new ArrayList<>(node.getAdjacentNodes()); + nodesToVisit.removeAll(exploredNodes); + if (nodesToVisit.isEmpty()) { + return true; + } + for (Node n : nodesToVisit) { + if (exclusionTypes.contains(n.getType())) { + return false; + } else if (typeStops.contains(n.getType())) { + nodesResult.add(n); + exploredNodes.add(n); + } else if (rDelimitedExploration(n, typeStops, exclusionTypes, nodesResult, + exploredNodes)) { + nodesResult.add(n); + } else { + return false; + } + } + return true; + } + + /** + * Check if the cell is a pure extern and return true in that case, else false (suspected shunt) + * + * @param cell : the cell to analyse + **/ + private boolean isPureExternCell(Graph graph, ExternCell cell) { + /*Explore the graph of the candidate cell. Remove successively one node, assess if it splits the graph into n>1 branches + if so, then check if each component is exclusively reaching FEEDER or exclusively reaching BUS + And verify you have at least one of them + Return true in that case else false meaning there is one shunt + */ + for (Node n : cell.getNodes()) { + List nodes = new ArrayList<>(cell.getNodes()); + nodes.remove(n); + List> connexComponents = graph.getConnexComponents(nodes); + if (checkExternComponents(connexComponents)) { + return true; + } + } + return false; + } + + /** + * @param connexComponents components partition to analyse + * @return true if this partition reflects an extern cell + */ + private boolean checkExternComponents(List> connexComponents) { + if (connexComponents.size() > 1) { + boolean hasDepartBranch = false; + boolean hasBusBranch = false; + boolean hasMixBranch = false; + for (List nodesConnex : connexComponents) { + List types = nodesConnex.stream() + .map(Node::getType) + .distinct().filter(t -> t == Node.NodeType.FEEDER || t == Node.NodeType.BUS) + .collect(Collectors.toList()); + if (types.size() == 2) { + hasMixBranch = true; + } else if (types.isEmpty()) { + return false; + } else if (types.get(0).equals(Node.NodeType.FEEDER)) { + hasDepartBranch = true; + } else { + hasBusBranch = true; + } + } + return hasBusBranch && hasDepartBranch && !hasMixBranch; + } + return false; + } + + /** + * @param cell the nodes of a cell that is suppected to be a shunt + **/ + private void detectAndTypeShunt(Graph graph, Cell cell) { + + List externalNodes = graph.getNodes() + .stream() + .filter(node -> !cell.getNodes().contains(node)) + .collect(Collectors.toList()); + + for (Node n : cell.getNodes().stream() + .filter(n -> n.getAdjacentNodes().size() > 2).collect(Collectors.toList())) { + // optimisation : a Shunt node has necessarily 3 ore more adjacent nodes + + List cellNodesExtern1 = checkCandidateShuntNode(n, externalNodes); + if (cellNodesExtern1 != null) { + // create the 1st new external cell + cell.removeAllNodes(cellNodesExtern1.stream() + .filter(m -> !m.getType().equals(Node.NodeType.BUS)) + .collect(Collectors.toList())); + n.setType(Node.NodeType.SHUNT); + ExternCell newExternCell = new ExternCell(graph); + cellNodesExtern1.add(n); + newExternCell.setNodes(cellNodesExtern1); + + //create the shunt cell + + ShuntCell shuntCell = createShuntCell(graph, n, cellNodesExtern1); + + // create the 2nd external cell + List cellNodesExtern2 = cell.getNodes().stream() + .filter(node -> (!cellNodesExtern1.contains(node) || node.getType() == Node.NodeType.BUS) + && (!shuntCell.getNodes().contains(node) || node.getType() == Node.NodeType.SHUNT)) + .collect(Collectors.toList()); + + cellNodesExtern2.removeAll(cellNodesExtern2.stream() + .filter(node -> node.getType() == Node.NodeType.BUS + && node.getAdjacentNodes().stream().noneMatch( + cellNodesExtern2::contains)) + .collect(Collectors.toList())); + + ExternCell newExternCell2 = new ExternCell(graph); + newExternCell2.setNodes(cellNodesExtern2); + + graph.removeCell(cell); +// TODO shuntCell.setBridgingCellsFromShuntNodes(); + break; + } + } + } + + private List checkCandidateShuntNode(Node n, List externalNodes) { + List kindToFilter = Arrays.asList(Node.NodeType.BUS, + Node.NodeType.FEEDER, + Node.NodeType.SHUNT); + /* + the node n is candidate to be a SHUNT node if there is + (i) at least one branch exclusively reaching BUSes + (ii) at least one branch exclusively reaching DEPARTs + (iii) at least one branch reaching BUSes and DEPARTs (this branch would be a Shunt) + In that case, the BUSes branches and DEPARTs Branches constitute an EXTERN Cell, + and returned in the cellNodesExtern + */ + + List visitedNodes = new ArrayList<>(externalNodes); + visitedNodes.add(n); //removal of the node to explore branches from it + + List cellNodesExtern = new ArrayList<>(); + boolean hasFeederBranch = false; + boolean hasBusBranch = false; + boolean hasMixBranch = false; + + List adjList = new ArrayList<>(n.getAdjacentNodes()); + adjList.removeAll(visitedNodes); + for (Node adj : adjList) { + if (!visitedNodes.contains(adj)) { + List resultNodes = new ArrayList<>(); + rDelimitedExploration(adj, + kindToFilter, + new ArrayList<>(), + resultNodes, + visitedNodes); + resultNodes.add(adj); + + List types = resultNodes.stream() // what are the types of terminal node of the branch + .map(Node::getType) + .distinct().filter(kindToFilter::contains) + .collect(Collectors.toList()); + + if (types.size() > 1) { + hasMixBranch = true; + } else { + hasBusBranch |= types.get(0).equals(Node.NodeType.BUS); + hasFeederBranch |= types.get(0).equals(Node.NodeType.FEEDER); + + if (types.get(0).equals(Node.NodeType.BUS) || types.get(0).equals(Node.NodeType.FEEDER)) { + cellNodesExtern.addAll(resultNodes); + } + } + visitedNodes.removeAll(resultNodes + .stream() + .filter(m -> m.getType().equals(Node.NodeType.BUS)) + .collect(Collectors.toList())); + + } + } + return (hasBusBranch && hasFeederBranch && hasMixBranch) ? cellNodesExtern : null; + } + + private ShuntCell createShuntCell(Graph graph, Node n, List cellNodesExtern1) { + List shuntCellNodes = new ArrayList<>(); + shuntCellNodes.add(n); + Node currentNode = n.getAdjacentNodes().stream() + .filter(node -> !cellNodesExtern1.contains(node)) + .findAny().orElse(null); + if (currentNode != null) { + while (currentNode.getAdjacentNodes().size() == 2) { + shuntCellNodes.add(currentNode); + currentNode = shuntCellNodes.contains(currentNode.getAdjacentNodes().get(0)) + ? currentNode.getAdjacentNodes().get(1) : currentNode.getAdjacentNodes().get(0); + } + shuntCellNodes.add(currentNode); + currentNode.setType(Node.NodeType.SHUNT); + } + ShuntCell shuntCell = new ShuntCell(graph); // the shunt branch is made of the remaining cells + the actual node n + shuntCell.setNodes(shuntCellNodes); + return shuntCell; + } + +} + diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/InfoCalcPoints.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/InfoCalcPoints.java new file mode 100644 index 000000000..160f0edc9 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/InfoCalcPoints.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.BusCell; + +import java.util.Map; + +/** + * @author Franck Lecuyer + */ +public class InfoCalcPoints { + private LayoutParameters layoutParam; + private BusCell.Direction dNode1; + private BusCell.Direction dNode2; + private Map nbSnakeLinesTopBottom; + private Map nbSnakeLinesBetween; + private double x1; + private double x2; + private double y1; + private double y2; + private double xMaxGraph; + private String idMaxGraph; + + public LayoutParameters getLayoutParam() { + return layoutParam; + } + + public void setLayoutParam(LayoutParameters layoutParam) { + this.layoutParam = layoutParam; + } + + public BusCell.Direction getdNode1() { + return dNode1; + } + + public void setdNode1(BusCell.Direction dNode1) { + this.dNode1 = dNode1; + } + + public BusCell.Direction getdNode2() { + return dNode2; + } + + public void setdNode2(BusCell.Direction dNode2) { + this.dNode2 = dNode2; + } + + public Map getNbSnakeLinesTopBottom() { + return nbSnakeLinesTopBottom; + } + + public void setNbSnakeLinesTopBottom(Map nbSnakeLinesTopBottom) { + this.nbSnakeLinesTopBottom = nbSnakeLinesTopBottom; + } + + public Map getNbSnakeLinesBetween() { + return nbSnakeLinesBetween; + } + + public void setNbSnakeLinesBetween(Map nbSnakeLinesBetween) { + this.nbSnakeLinesBetween = nbSnakeLinesBetween; + } + + public double getX1() { + return x1; + } + + public void setX1(double x1) { + this.x1 = x1; + } + + public double getX2() { + return x2; + } + + public void setX2(double x2) { + this.x2 = x2; + } + + public double getY1() { + return y1; + } + + public void setY1(double y1) { + this.y1 = y1; + } + + public double getY2() { + return y2; + } + + public void setY2(double y2) { + this.y2 = y2; + } + + public double getxMaxGraph() { + return xMaxGraph; + } + + public void setxMaxGraph(double xMaxGraph) { + this.xMaxGraph = xMaxGraph; + } + + public String getIdMaxGraph() { + return idMaxGraph; + } + + public void setIdMaxGraph(String idMaxGraph) { + this.idMaxGraph = idMaxGraph; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LayoutParameters.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LayoutParameters.java new file mode 100644 index 000000000..7ab302b2e --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/LayoutParameters.java @@ -0,0 +1,358 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class LayoutParameters { + + private double translateX = 20; + private double translateY = 50; + private double initialXBus = 0; + private double initialYBus = 260; + private double verticalSpaceBus = 25; + private double horizontalBusPadding = 20; + + private double cellWidth = 50; + + private double externCellHeight = 250; + private double internCellHeight = 40; + + private double stackHeight = 30; + + private boolean showGrid = false; + + private boolean showInternalNodes = false; + + private double scaleFactor = 1; + + private double horizontalSubstationPadding = 50; + private double verticalSubstationPadding = 50; + + private String diagramName = null; + + private boolean drawStraightWires = false; + + private double horizontalSnakeLinePadding = 20; + private double verticalSnakeLinePadding = 25; + private double arrowDistance = 20; + + private boolean showInductorFor3WT = false; + + private boolean shiftFeedersPosition = false; + + private double scaleShiftFeedersPosition = 1; + private boolean avoidSVGComponentsDuplication = false; + + @JsonCreator + public LayoutParameters() { + } + + @JsonCreator + public LayoutParameters(@JsonProperty("translateX") double translateX, + @JsonProperty("translateY") double translateY, + @JsonProperty("initialXBus") double initialXBus, + @JsonProperty("initialYBus") double initialYBus, + @JsonProperty("verticalSpaceBus") double verticalSpaceBus, + @JsonProperty("horizontalBusPadding") double horizontalBusPadding, + @JsonProperty("cellWidth") double cellWidth, + @JsonProperty("externCellHeight") double externCellHeight, + @JsonProperty("internCellHeight") double internCellHeight, + @JsonProperty("stackHeight") double stackHeight, + @JsonProperty("showGrid") boolean showGrid, + @JsonProperty("showInternalNodes") boolean showInternalNodes, + @JsonProperty("scaleFactor") double scaleFactor, + @JsonProperty("horizontalSubstationPadding") double horizontalSubstationPadding, + @JsonProperty("verticalSubstationPadding") double verticalSubstationPadding, + @JsonProperty("drawStraightWires") boolean drawStraightWires, + @JsonProperty("horizontalSnakeLinePadding") double horizontalSnakeLinePadding, + @JsonProperty("verticalSnakeLinePadding") double verticalSnakeLinePadding, + @JsonProperty("arrowDistance") double arrowDistance, + @JsonProperty("showInductorFor3WT") boolean showInductorFor3WT, + @JsonProperty("diagramName") String diagramName, + @JsonProperty("shiftFeedersPosition") boolean shiftFeedersPosition, + @JsonProperty("scaleShiftFeedersPosition") double scaleShiftFeedersPosition, + @JsonProperty("avoidSVGComponentsDuplication") boolean avoidSVGComponentsDuplication) { + this.translateX = translateX; + this.translateY = translateY; + this.initialXBus = initialXBus; + this.initialYBus = initialYBus; + this.verticalSpaceBus = verticalSpaceBus; + this.horizontalBusPadding = horizontalBusPadding; + this.cellWidth = cellWidth; + this.externCellHeight = externCellHeight; + this.internCellHeight = internCellHeight; + this.stackHeight = stackHeight; + this.showGrid = showGrid; + this.showInternalNodes = showInternalNodes; + this.scaleFactor = scaleFactor; + this.horizontalSubstationPadding = horizontalSubstationPadding; + this.verticalSubstationPadding = verticalSubstationPadding; + this.drawStraightWires = drawStraightWires; + this.horizontalSnakeLinePadding = horizontalSnakeLinePadding; + this.verticalSnakeLinePadding = verticalSnakeLinePadding; + this.arrowDistance = arrowDistance; + this.showInductorFor3WT = showInductorFor3WT; + this.diagramName = diagramName; + this.shiftFeedersPosition = shiftFeedersPosition; + this.scaleShiftFeedersPosition = scaleShiftFeedersPosition; + this.avoidSVGComponentsDuplication = avoidSVGComponentsDuplication; + } + + public LayoutParameters(LayoutParameters other) { + Objects.requireNonNull(other); + translateX = other.translateX; + translateY = other.translateY; + initialXBus = other.initialXBus; + initialYBus = other.initialYBus; + verticalSpaceBus = other.verticalSpaceBus; + horizontalBusPadding = other.horizontalBusPadding; + cellWidth = other.cellWidth; + externCellHeight = other.externCellHeight; + internCellHeight = other.internCellHeight; + stackHeight = other.stackHeight; + showGrid = other.showGrid; + showInternalNodes = other.showInternalNodes; + scaleFactor = other.scaleFactor; + horizontalSubstationPadding = other.horizontalSubstationPadding; + verticalSubstationPadding = other.verticalSubstationPadding; + drawStraightWires = other.drawStraightWires; + horizontalSnakeLinePadding = other.horizontalSnakeLinePadding; + verticalSnakeLinePadding = other.verticalSnakeLinePadding; + arrowDistance = other.arrowDistance; + showInductorFor3WT = other.showInductorFor3WT; + diagramName = other.diagramName; + shiftFeedersPosition = other.shiftFeedersPosition; + scaleShiftFeedersPosition = other.scaleShiftFeedersPosition; + avoidSVGComponentsDuplication = other.avoidSVGComponentsDuplication; + } + + public double getTranslateX() { + return translateX; + } + + public LayoutParameters setTranslateX(double translateX) { + this.translateX = translateX; + return this; + } + + public double getTranslateY() { + return translateY; + } + + public LayoutParameters setTranslateY(double translateY) { + this.translateY = translateY; + return this; + } + + public double getInitialXBus() { + return initialXBus; + } + + public LayoutParameters setInitialXBus(double initialXBus) { + this.initialXBus = initialXBus; + return this; + } + + public double getInitialYBus() { + return initialYBus; + } + + public LayoutParameters setInitialYBus(double initialYBus) { + this.initialYBus = initialYBus; + return this; + } + + public double getVerticalSpaceBus() { + return verticalSpaceBus; + } + + public LayoutParameters setVerticalSpaceBus(double verticalSpaceBus) { + this.verticalSpaceBus = verticalSpaceBus; + return this; + } + + public double getHorizontalBusPadding() { + return horizontalBusPadding; + } + + public LayoutParameters setHorizontalBusPadding(double horizontalSpaceBus) { + this.horizontalBusPadding = horizontalSpaceBus; + return this; + } + + public double getCellWidth() { + return cellWidth; + } + + public LayoutParameters setCellWidth(double cellWidth) { + this.cellWidth = cellWidth; + return this; + } + + public double getExternCellHeight() { + return externCellHeight; + } + + public LayoutParameters setExternCellHeight(double externCellHeight) { + this.externCellHeight = externCellHeight; + return this; + } + + public double getInternCellHeight() { + return internCellHeight; + } + + public LayoutParameters setInternCellHeight(double internCellHeight) { + this.internCellHeight = internCellHeight; + return this; + } + + public double getStackHeight() { + return stackHeight; + } + + public LayoutParameters setStackHeight(double stackHeight) { + this.stackHeight = stackHeight; + return this; + } + + public boolean isShowGrid() { + return showGrid; + } + + public LayoutParameters setShowGrid(boolean showGrid) { + this.showGrid = showGrid; + return this; + } + + public boolean isShowInternalNodes() { + return showInternalNodes; + } + + public LayoutParameters setShowInternalNodes(boolean showInternalNodes) { + this.showInternalNodes = showInternalNodes; + return this; + } + + public double getScaleFactor() { + return scaleFactor; + } + + public LayoutParameters setScaleFactor(double scaleFactor) { + this.scaleFactor = scaleFactor; + return this; + } + + public double getHorizontalSubstationPadding() { + return horizontalSubstationPadding; + } + + public LayoutParameters setHorizontalSubstationPadding(double padding) { + this.horizontalSubstationPadding = padding; + return this; + } + + public double getVerticalSubstationPadding() { + return verticalSubstationPadding; + } + + public LayoutParameters setVerticalSubstationPadding(double padding) { + this.verticalSubstationPadding = padding; + return this; + } + + public String getDiagramName() { + return diagramName; + } + + public LayoutParameters setDiagramName(String diagramName) { + this.diagramName = diagramName; + return this; + } + + public boolean isDrawStraightWires() { + return drawStraightWires; + } + + public LayoutParameters setDrawStraightWires(boolean drawStraightWires) { + this.drawStraightWires = drawStraightWires; + return this; + } + + public double getHorizontalSnakeLinePadding() { + return horizontalSnakeLinePadding; + } + + public LayoutParameters setHorizontalSnakeLinePadding(double horizontalSnakeLinePadding) { + this.horizontalSnakeLinePadding = horizontalSnakeLinePadding; + return this; + } + + public double getVerticalSnakeLinePadding() { + return verticalSnakeLinePadding; + } + + public LayoutParameters setVerticalSnakeLinePadding(double verticalSnakeLinePadding) { + this.verticalSnakeLinePadding = verticalSnakeLinePadding; + return this; + } + + public double getArrowDistance() { + return arrowDistance; + } + + public LayoutParameters setArrowDistance(double arrowDistance) { + this.arrowDistance = arrowDistance; + return this; + } + + public boolean isShowInductorFor3WT() { + return showInductorFor3WT; + } + + public LayoutParameters setShowInductorFor3WT(boolean showInductorFor3WT) { + this.showInductorFor3WT = showInductorFor3WT; + return this; + } + + public boolean isShiftFeedersPosition() { + return shiftFeedersPosition; + } + + public LayoutParameters setShiftFeedersPosition(boolean shiftFeedersPosition) { + this.shiftFeedersPosition = shiftFeedersPosition; + return this; + } + + public double getScaleShiftFeedersPosition() { + return scaleShiftFeedersPosition; + } + + public LayoutParameters setScaleShiftFeedersPosition(double scaleShiftFeedersPosition) { + this.scaleShiftFeedersPosition = scaleShiftFeedersPosition; + return this; + } + + public boolean isAvoidSVGComponentsDuplication() { + return avoidSVGComponentsDuplication; + } + + public LayoutParameters setAvoidSVGComponentsDuplication(boolean avoidSVGComponentsDuplication) { + this.avoidSVGComponentsDuplication = avoidSVGComponentsDuplication; + return this; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionByClustering.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionByClustering.java new file mode 100644 index 000000000..8108ba384 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionByClustering.java @@ -0,0 +1,691 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.sld.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + */ + +// WE ASSUME THAT IT IS POSSIBLE TO STACK ALL CELLS AND BE ABLE TO ORGANISE THE VOLTAGELEVEL CONSISTENTLY + +public class PositionByClustering implements PositionFinder { + + private static final Logger LOGGER = LoggerFactory.getLogger(PositionByClustering.class); + + private static final BusCell.Direction DEFAULTDIRECTION = BusCell.Direction.TOP; + + private class Context { + private final Graph graph; + private final Map nodeToNb = new HashMap<>(); + private final List legBusSets = new ArrayList<>(); + private final List lbsClusterSet = new ArrayList<>(); + private final List lbsLinks = new ArrayList<>(); + + public Context(Graph graph) { + this.graph = Objects.requireNonNull(graph); + } + } + + @Override + public void buildLayout(Graph graph) { + LOGGER.info("start BuildLayout"); + Context context = new Context(graph); + + indexBusPosition(context); + + initLegBusSets(context.graph, context.legBusSets, context.nodeToNb); + linkLegBusSets(context); + + clustering(context); + establishBusPositions(context); + establishFeederPositions(context); + + graph.setMaxBusPosition(); + forceSameOrientationForShuntedCell(graph); + } + + private void indexBusPosition(Context context) { + int i = 1; + for (BusNode n : new ArrayList<>(context.graph.getNodeBuses())) { + context.nodeToNb.put(n, i); + i++; + } + } + + private void initLegBusSets(Graph graph, List legBusSets, Map nodeToNb) { + graph.getCells().stream() + .filter(cell -> cell instanceof BusCell) + .map(BusCell.class::cast) + .forEach(cell -> { + if (cell.getType() == Cell.CellType.INTERN && !((InternCell) cell).isUniLeg()) { + pushNewLBS(legBusSets, nodeToNb, cell, Side.LEFT); + pushNewLBS(legBusSets, nodeToNb, cell, Side.RIGHT); + } else { + pushNewLBS(legBusSets, nodeToNb, cell, Side.UNDEFINED); + } + }); + legBusSets.forEach(LegBusSet::checkInternCells); + } + + private void pushNewLBS(List legBusSets, Map nodeToNb, BusCell busCell, Side side) { + LegBusSet legBusSet = side == Side.UNDEFINED ? + new LegBusSet(nodeToNb, busCell) : + new LegBusSet(nodeToNb, (InternCell) busCell, side); + + for (LegBusSet lbs : legBusSets) { + if (lbs.contains(legBusSet)) { + lbs.absorbs(legBusSet); + return; + } + } + List absorbedLBS = new ArrayList<>(); + legBusSets.forEach(lbs -> { + if (legBusSet.contains(lbs)) { + absorbedLBS.add(lbs); + legBusSet.absorbs(lbs); + } + }); + legBusSets.removeAll(absorbedLBS); + legBusSets.add(legBusSet); + } + + private void linkLegBusSets(Context context) { + for (int i = 0; i < context.legBusSets.size(); i++) { + for (int j = i + 1; j < context.legBusSets.size(); j++) { + new LBSLink(context.lbsLinks, context.legBusSets.get(i), context.legBusSets.get(j)); + } + } + context.lbsLinks.sort(Collections.reverseOrder()); + } + + private void clustering(Context context) { + context.legBusSets.forEach(lbs -> new LBSCluster(context.lbsClusterSet, lbs)); + + // Cluster with links: stronger links first + List linksToHandle = context.lbsLinks.stream() + .filter(LBSLink::hasLink) + .collect(Collectors.toList()); + for (LBSLink lbsLink : linksToHandle) { + lbsLink.tryToMergeClusters(); + } + LBSCluster mainCluster = context.lbsClusterSet.get(0); + + // Merge Cluster with no link + while (context.lbsClusterSet.size() != 1) { + mainCluster.merge(Side.RIGHT, context.lbsClusterSet.get(1), Side.LEFT); + } + } + + private void establishBusPositions(Context context) { + context.graph.getNodeBuses().forEach(busNode -> busNode.setStructuralPosition(null)); + LBSCluster finalCluster = context.lbsClusterSet.get(0); + int v = 1; + Set remainingBuses = new HashSet<>(context.graph.getNodeBuses()); + while (!remainingBuses.isEmpty()) { + buildLane(finalCluster, remainingBuses, v); + v++; + } + } + + /** + * BusNodeAndLbsIndex holds the index of the LegBusSet in the LBSCluster, and the node that is to be positioned + */ + class BusNodeAndLbsIndex { + BusNode busNode = null; + int lbsIndex = 0; + } + + private void buildLane(LBSCluster lbsCluster, Set remainingBuses, int v) { + Set busOnLeftSide = new HashSet<>(); + int h = 1; + List lbsList = new ArrayList<>(lbsCluster.getLbsList()); + BusNodeAndLbsIndex busIndex = new BusNodeAndLbsIndex(); + while (busIndex.lbsIndex < lbsList.size()) { + if (busIndex.busNode == null) { + findABusToPositionInNextLbs(lbsList, busIndex, remainingBuses, busOnLeftSide); + } + if (busIndex.busNode != null) { + busIndex.busNode.setStructuralPosition(new Position(h, v)); + h++; + remainingBuses.remove(busIndex.busNode); + int actualIndex = busIndex.lbsIndex; + getLastIndexContainingCurrentBus(busIndex, lbsList); + updateBusOnLeftSide(busOnLeftSide, lbsCluster.getLbsList(), actualIndex, busIndex.lbsIndex); + actualIndex = busIndex.lbsIndex; + if (getConnectedBusThroughFlatCell(lbsList, busIndex, remainingBuses, busOnLeftSide)) { + updateBusOnLeftSide(busOnLeftSide, lbsCluster.getLbsList(), actualIndex, busIndex.lbsIndex); + } else { + busIndex.busNode = null; + busIndex.lbsIndex++; + } + } + } + } + + void findABusToPositionInNextLbs(List legBusSetList, + BusNodeAndLbsIndex busIndex, + Set remainingBuses, + Set busOnLeftSide) { + for (int i = busIndex.lbsIndex; i < legBusSetList.size(); i++) { + busIndex.lbsIndex = i; + for (BusNode bus : legBusSetList.get(busIndex.lbsIndex).getBusNodeSet()) { + if (remainingBuses.contains(bus) && !busOnLeftSide.contains(bus)) { + busIndex.busNode = bus; + return; + } + } + } + busIndex.lbsIndex++; // this index is out of range of LegBusSetList, and end the while loop in which it is called + } + + private void getLastIndexContainingCurrentBus(BusNodeAndLbsIndex busIndex, List lbsList) { + int j = lbsList.size() - 1; + while (j >= busIndex.lbsIndex) { + if (lbsList.get(j).getBusNodeSet().contains(busIndex.busNode)) { + break; + } + j--; + } + busIndex.lbsIndex = j; + } + + private void updateBusOnLeftSide(Set busOnLeftSide, List legBusSets, int index1, + int index2) { + for (int i = index1; i <= Math.min(index2, legBusSets.size() - 1); i++) { + busOnLeftSide.addAll(legBusSets.get(i).getBusNodeSet()); + } + } + + private boolean getConnectedBusThroughFlatCell(List legBusSetList, + BusNodeAndLbsIndex busIndex, + Set remainingBuses, + Set busOnLeftSide) { + BusNode node = legBusSetList.get(busIndex.lbsIndex) + .getCandidateFlatCells().keySet().stream() + .filter(internCell -> internCell.getBusNodes().contains(busIndex.busNode)) + .flatMap(internCell -> internCell.getBusNodes().stream()) + .filter(busNode -> busNode != busIndex.busNode + && remainingBuses.contains(busNode) + && !busOnLeftSide.contains(busNode)) + .findAny() + .orElse(null); + + if (node != null) { + busIndex.busNode = node; + int j = busIndex.lbsIndex; + while (j < legBusSetList.size()) { + if (legBusSetList.get(j).getBusNodeSet().contains(busIndex.busNode)) { + break; + } + j++; + } + busIndex.lbsIndex = j; + return true; + } else { + return false; + } + } + + private void establishFeederPositions(Context context) { + int cellPos = 0; + int feederPosition = 1; + for (LegBusSet lbs : context.lbsClusterSet.get(0).getLbsList()) { + for (ExternCell busCell : lbs.getEmbededCells().stream() + .filter(busCell -> busCell.getType() == Cell.CellType.EXTERN) + .map(ExternCell.class::cast) + .collect(Collectors.toSet())) { + busCell.setDirection(cellPos % 2 == 0 ? BusCell.Direction.TOP : BusCell.Direction.BOTTOM); + busCell.setOrder(cellPos); + cellPos++; + for (FeederNode feederNode : busCell.getNodes().stream() + .filter(n -> n.getType() == Node.NodeType.FEEDER) + .map(FeederNode.class::cast).collect(Collectors.toList())) { + feederNode.setOrder(feederPosition); + feederPosition++; + } + } + } + } + + private class LegBusSet { + private Set busNodeSet; + private Set embededCells; + private Map candidateFlatCells; + private Map crossoverInternCells; + private Map linksTolbs; + LBSCluster lbsCluster; + + LegBusSet(Map nodeToNb, List busNodes) { + busNodeSet = new TreeSet<>(Comparator.comparingInt(nodeToNb::get)); + busNodeSet.addAll(busNodes); + embededCells = new HashSet<>(); + candidateFlatCells = new HashMap<>(); + crossoverInternCells = new HashMap<>(); + linksTolbs = new TreeMap<>(); + } + + LegBusSet(Map nodeToNb, BusCell cell) { + this(nodeToNb, cell.getBusNodes()); + embededCells.add(cell); + } + + LegBusSet(Map nodeToNb, InternCell internCell, Side side) { + this(nodeToNb, internCell.getSideBusNodes(side)); + if (internCell.getBusNodes().size() == 1) { + embededCells.add(internCell); + } else if (internCell.getBusNodes().size() == 2) { + candidateFlatCells.put(internCell, side); + } else { + crossoverInternCells.put(internCell, side); + } + } + + boolean contains(LegBusSet lbs) { + return busNodeSet.containsAll(lbs.getBusNodeSet()); + } + + void absorbs(LegBusSet lbsToAbsorb) { + busNodeSet.addAll(lbsToAbsorb.getBusNodeSet()); + embededCells.addAll(lbsToAbsorb.getEmbededCells()); + absorbMap(candidateFlatCells, lbsToAbsorb.getCandidateFlatCells()); + absorbMap(crossoverInternCells, lbsToAbsorb.getCrossoverInternCell()); + } + + void absorbMap(Map myMap, Map map) { + Set commonCells = new HashSet<>(myMap.keySet()); + Set cellToAbsorb = map.keySet(); + commonCells.retainAll(cellToAbsorb); + for (InternCell commonCell : commonCells) { + if (myMap.get(commonCell) == Side.RIGHT && map.get(commonCell) == Side.LEFT + || myMap.get(commonCell) == Side.LEFT && map.get(commonCell) == Side.RIGHT) { + embededCells.add(commonCell); + myMap.remove(commonCell); + } else { + throw new PowsyblException("Absorption of InternCell in a LegBusSet should concern both side of the InternCell"); + } + } + cellToAbsorb.removeAll(commonCells); + cellToAbsorb.forEach(internCell -> myMap.put(internCell, map.get(internCell))); + } + + void checkInternCells() { + genericCheckInternCells(candidateFlatCells); + genericCheckInternCells(crossoverInternCells); + } + + private void genericCheckInternCells(Map cells) { + List cellActuallyEmbeded = new ArrayList<>(); + cells.forEach((internCell, side) -> { + List otherLegBusNodes = internCell + .getSideBusNodes(side.getFlip()); + if (busNodeSet.containsAll(otherLegBusNodes)) { + cellActuallyEmbeded.add(internCell); + } + }); + cellActuallyEmbeded.forEach(cells::remove); + embededCells.addAll(cellActuallyEmbeded); + } + + void addLink(LBSLink lbsLink) { + linksTolbs.put(lbsLink, lbsLink.getOtherLBS(this)); + } + + void setLbsCluster(LBSCluster lbsCluster) { + this.lbsCluster = lbsCluster; + } + + LBSCluster getCluster() { + return lbsCluster; + } + + Side getMySidInCluster() { + return lbsCluster.getLbsSide(this); + } + + Map getCandidateFlatCells() { + return candidateFlatCells; + } + + Map getCrossoverInternCell() { + return crossoverInternCells; + } + + Set getBusNodeSet() { + return busNodeSet; + } + + public Set getEmbededCells() { + return embededCells; + } + + @Override + public boolean equals(Object o) { + if (o instanceof LegBusSet) { + return busNodeSet.equals(((LegBusSet) o).busNodeSet); + } else { + return false; + } + } + + @Override + public int hashCode() { + return busNodeSet.hashCode(); + } + + } + + private enum LinkCategory { + COMMONBUSES, FLATCELLS, CROSSOVER//, SHUNT + } + + private class LBSLink implements Comparable { + LegBusSet[] lbss = new LegBusSet[2]; + Map categoryToWeight = new EnumMap<>(LinkCategory.class); + + LBSLink(List lbsLinks, LegBusSet lbs1, LegBusSet lbs2) { + lbss[0] = lbs1; + lbss[1] = lbs2; + assessLink(); + lbsLinks.add(this); + } + + void assessLink() { + HashSet nodeBusesIntersect = new HashSet<>(lbss[0].getBusNodeSet()); + nodeBusesIntersect.retainAll(lbss[1].getBusNodeSet()); + categoryToWeight.put(LinkCategory.COMMONBUSES, nodeBusesIntersect.size()); + + HashSet flatCellIntersect = new HashSet<>(lbss[0].getCandidateFlatCells().keySet()); + flatCellIntersect.retainAll(lbss[1].getCandidateFlatCells().keySet()); + categoryToWeight.put(LinkCategory.FLATCELLS, flatCellIntersect.size()); + + HashSet commonInternCells = new HashSet<>(lbss[0].getCrossoverInternCell().keySet()); + commonInternCells.retainAll(lbss[1].getCrossoverInternCell().keySet()); + categoryToWeight.put(LinkCategory.CROSSOVER, (int) (commonInternCells + .stream() + .flatMap(internCell -> internCell.getBusNodes().stream()) + .distinct() + .count())); + + for (LinkCategory cat : LinkCategory.values()) { + if (categoryToWeight.get(cat) != 0) { + lbss[0].addLink(this); + lbss[1].addLink(this); + break; + } + } + } + + int getLinkCategoryWeight(LinkCategory cat) { + return categoryToWeight.get(cat); + } + + LegBusSet getOtherLBS(LegBusSet lbs) { + if (lbs == lbss[0]) { + return lbss[1]; + } + if (lbs == lbss[1]) { + return lbss[0]; + } + return null; + } + + LegBusSet getLbs(int i) { + if (i > 1) { + return null; + } + return lbss[i]; + } + + boolean tryToMergeClusters() { + if (lbss[0].getCluster() == lbss[1].getCluster() + || lbss[0].getMySidInCluster() == Side.UNDEFINED + || lbss[1].getMySidInCluster() == Side.UNDEFINED) { + return false; + } + lbss[0].getCluster().merge(lbss[0].getMySidInCluster(), + lbss[1].getCluster(), lbss[1].getMySidInCluster()); + return true; + } + + boolean hasLink() { + return categoryToWeight.values().stream().mapToInt(Integer::intValue).sum() != 0; + } + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public int compareTo(@Nonnull Object o) { + if (!(o instanceof LBSLink)) { + return 0; + } + LBSLink lbsLink = (LBSLink) o; + for (LinkCategory category : LinkCategory.values()) { + if (lbsLink.getLinkCategoryWeight(category) > getLinkCategoryWeight(category)) { + return -1; + } + if (lbsLink.getLinkCategoryWeight(category) < getLinkCategoryWeight(category)) { + return 1; + } + } + return 0; + } + } + +/* + private class LBSClusterLink implements Comparable { + LBSCluster[] lbsClusters = new LBSCluster[2]; + Map categoryToWeight = new EnumMap<>(LinkCategory.class); + + LBSClusterLink(LBSCluster lbsCluster1, LBSCluster lbsCluster2, Set LBSClusterLinks) { + lbsClusters[0] = lbsCluster1; + lbsClusters[1] = lbsCluster2; + LBSClusterLinks.add(this); + assessLink(); + } + + void assessLink() { + HashSet nodeBusesIntersect = new HashSet<>(lbsClusters[0].getNodeBuses()); + nodeBusesIntersect.retainAll(lbsClusters[1].getNodeBuses()); + categoryToWeight.put(LinkCategory.COMMONBUSES, nodeBusesIntersect.size()); + + HashSet flatCellIntersect = new HashSet<>(lbsClusters[0].getCandidateFlatCells()); + flatCellIntersect.retainAll(lbsClusters[1].getCandidateFlatCells()); + categoryToWeight.put(LinkCategory.FLATCELLS, flatCellIntersect.size()); + + HashSet commonInternCells = new HashSet<>(lbsClusters[0].getCrossoverCells()); + commonInternCells.retainAll(lbsClusters[1].getCrossoverCells()); + categoryToWeight.put(LinkCategory.CROSSOVER, (int) (commonInternCells + .stream() + .flatMap(internCell -> internCell.getBusNodes().stream()) + .distinct() + .count())); + } + + int getLinkCategoryWeight(LinkCategory cat) { + return categoryToWeight.get(cat); + } + + LBSCluster getOtherLBS(LBSCluster lbsCluster) { + if (lbsCluster == lbsClusters[0]) { + return lbsClusters[1]; + } + if (lbsCluster == lbsClusters[1]) { + return lbsClusters[0]; + } + return null; + } + + LBSCluster getLbs(int i) { + if (i > 1) { + return null; + } + return lbsClusters[i]; + } + + boolean tryToMergeClusters() { + if (lbsClusters[0] == lbsClusters[1] + || lbsClusters[0].getMySidInCluster() == Side.UNDEFINED + || lbsClusters[1].getMySidInCluster() == Side.UNDEFINED) { + return false; + } + lbsClusters[0].getCluster().merge(lbsClusters[0].getMySidInCluster(), + lbsClusters[1].getCluster(), lbsClusters[1].getMySidInCluster()); + return true; + } + + + @Override + public boolean equals(Object obj) { + return super.equals(obj); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public int compareTo(@Nonnull Object o) { + if (!(o instanceof LBSLink)) { + return 0; + } + LBSLink lbsLink = (LBSLink) o; + for (LinkCategory category : LinkCategory.values()) { + if (lbsLink.getLinkCategoryWeight(category) > getLinkCategoryWeight(category)) { + return -1; + } + if (lbsLink.getLinkCategoryWeight(category) < getLinkCategoryWeight(category)) { + return 1; + } + } + return 0; + } + } +*/ + + private class HorizontalLane { + List busNodes; + + HorizontalLane(BusNode busNode) { + busNodes = new ArrayList<>(); + busNodes.add(busNode); + } + + void reverse() { + Collections.reverse(busNodes); + } + + BusNode getSideNode(Side side) { + if (side == Side.LEFT) { + return busNodes.get(0); + } + if (side == Side.RIGHT) { + return busNodes.get(busNodes.size() - 1); + } + return null; + } + } + + private class LBSCluster { + List lbsList; + Map sideToLbs; + List horizontalLanes; + + List lbsClusters; + + LBSCluster(List lbsClusters, LegBusSet lbs) { + lbsList = new ArrayList<>(); + lbsList.add(lbs); + lbs.setLbsCluster(this); + + horizontalLanes = new ArrayList<>(); + lbs.getBusNodeSet().forEach(busNode -> horizontalLanes.add(new HorizontalLane(busNode))); + + sideToLbs = new EnumMap<>(Side.class); + sideToLbs.put(Side.LEFT, lbs); + sideToLbs.put(Side.RIGHT, lbs); + + this.lbsClusters = lbsClusters; + this.lbsClusters.add(this); + } + + void merge(Side myConcernedSide, LBSCluster otherLbsCluster, Side otherSide) { + if (myConcernedSide == Side.LEFT) { + reverse(); + } + if (otherSide == Side.RIGHT) { + otherLbsCluster.reverse(); + } + otherLbsCluster.getLbsList().forEach(legBusSet -> legBusSet.setLbsCluster(this)); + lbsList.addAll(otherLbsCluster.lbsList); + sideToLbs.put(Side.RIGHT, otherLbsCluster.sideToLbs.get(Side.RIGHT)); + lbsClusters.remove(otherLbsCluster); + } + + void reverse() { + Collections.reverse(lbsList); + LegBusSet lbs = sideToLbs.get(Side.LEFT); + sideToLbs.put(Side.LEFT, sideToLbs.get(Side.RIGHT)); + sideToLbs.put(Side.RIGHT, lbs); + horizontalLanes.forEach(HorizontalLane::reverse); + } + + Side getLbsSide(LegBusSet lbs) { + if (sideToLbs.get(Side.RIGHT) == lbs) { + return Side.RIGHT; + } + if (sideToLbs.get(Side.LEFT) == lbs) { + return Side.LEFT; + } + return Side.UNDEFINED; + } + + List getSideConnectableBuses(Side side) { + if (side == Side.LEFT || side == Side.RIGHT) { + return horizontalLanes.stream() + .map(horizontalLane -> horizontalLane.getSideNode(side)) + .collect(Collectors.toList()); + } + return null; + } + + Set getNodeBuses() { + return lbsList.stream().flatMap(legBusSet -> legBusSet.getBusNodeSet().stream()).collect(Collectors.toSet()); + } + + Set getCandidateFlatCells() { + return lbsList.stream().flatMap(legBusSet -> legBusSet.getCandidateFlatCells().keySet().stream()).collect(Collectors.toSet()); + } + + Set getCrossoverCells() { + return lbsList.stream().flatMap(legBusSet -> legBusSet.getCrossoverInternCell().keySet() + .stream()).collect(Collectors.toSet()); + } + + List getLbsList() { + return lbsList; + } + + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFinder.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFinder.java new file mode 100644 index 000000000..2460ff612 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFinder.java @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.Cell; +import com.powsybl.sld.model.ExternCell; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.Node; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * a PositionFinder determines: + *

    + *
  • the positions of nodeBuses
  • + *
  • cell order and direction of each cell connected to Bus (ie all cells except Shunt ones)
  • + *
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface PositionFinder { + + void buildLayout(Graph graph); + + default void forceSameOrientationForShuntedCell(Graph graph) { + for (Cell cell : graph.getCells().stream() + .filter(c -> c.getType() == Cell.CellType.SHUNT).collect(Collectors.toList())) { + List shNodes = cell.getNodes().stream() + .filter(node -> node.getType() == Node.NodeType.SHUNT).collect(Collectors.toList()); + ((ExternCell) shNodes.get(1).getCell()).setDirection( + ((ExternCell) shNodes.get(0).getCell()).getDirection()); + } + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFree.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFree.java new file mode 100644 index 000000000..cc526ccb5 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFree.java @@ -0,0 +1,586 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ + +// WE ASSUME THAT IT IS POSSIBLE TO STACK ALL CELLS AND BE ABLE TO ORGANISE THE VOLTAGELEVAL CONSITENTLY + +public class PositionFree implements PositionFinder { + + private static final Logger LOGGER = LoggerFactory.getLogger(PositionFree.class); + + private static final BusCell.Direction DEFAULTDIRECTION = BusCell.Direction.TOP; + + private class Context { + private final Graph graph; + private final Map nodeToNb = new HashMap<>(); + private final Map> vbcpToCells = new HashMap<>(); + private List hChains; + private final Map busToBelonging = new HashMap<>(); + private final List connectedClusters = new ArrayList<>(); + + public Context(Graph graph) { + this.graph = Objects.requireNonNull(graph); + } + } + + @Override + public void buildLayout(Graph graph) { + LOGGER.info("start BuildLayout"); + + Context context = new Context(graph); + + indexBusPosition(context); + initVbpcToCell(context); + organizeWithInternCells(context); +// +// newStructuralPosition(context); +// initiateFeederPosition(context); + + graph.setMaxBusPosition(); + forceSameOrientationForShuntedCell(graph); + } + + private void indexBusPosition(Context context) { + int i = 1; + for (BusNode n : new ArrayList<>(context.graph.getNodeBuses())) { + context.nodeToNb.put(n, i); + i++; + } + } + + private void initiateFeederPosition(Context context) { + int i = 0; + for (FeederNode feederNode : context.graph.getNodes().stream() + .filter(node -> node.getType() == Node.NodeType.FEEDER) + .map(FeederNode.class::cast) + .sorted(Comparator.comparing(Node::getId)) + .collect(Collectors.toList())) { + if (feederNode.getCell() != null) { + ((ExternCell) feederNode.getCell()).setDirection(DEFAULTDIRECTION); + feederNode.setOrder(12 * i); + i++; + } + } + context.graph.getCells().stream() + .filter(cell -> cell.getType() == Cell.CellType.EXTERN) + .map(ExternCell.class::cast) + .forEach(ExternCell::orderFromFeederOrders); + } + + private void initVbpcToCell(Context context) { + context.graph.getCells().stream() + .filter(cell -> cell.getType() == Cell.CellType.EXTERN) + .map(ExternCell.class::cast) + .forEach(cell -> addBusNodeSet(cell.getBusNodes(), cell, context)); + } + + private void addBusNodeSet(List busNodes, ExternCell externCell, Context context) { + VerticalBusConnectionPattern vbcp = new VerticalBusConnectionPattern(context, busNodes); + VerticalBusConnectionPattern targetBcp = null; + for (VerticalBusConnectionPattern vbcp1 : new ArrayList<>(context.vbcpToCells.keySet())) { + if (vbcp.isIncludedIn(vbcp1)) { + targetBcp = vbcp1; + } else if (vbcp1.isIncludedIn(vbcp)) { + context.vbcpToCells.putIfAbsent(vbcp, new ArrayList<>()); + context.vbcpToCells.get(vbcp).addAll(context.vbcpToCells.get(vbcp1)); + context.vbcpToCells.remove(vbcp1); + targetBcp = vbcp; + } + } + if (targetBcp == null) { + context.vbcpToCells.put(vbcp, new ArrayList<>()); + targetBcp = vbcp; + } + + if (externCell != null) { + context.vbcpToCells.get(targetBcp).add(externCell); + } + } + + private void addBusNodeSet(List busNodes, Context context) { + addBusNodeSet(busNodes, null, context); + } + + private void organizeWithInternCells(Context context) { + List structuringInternCells = identifyStructuringCells(context); + List candidateFlatCell = structuringInternCells.stream() + .filter(internCell -> internCell.getBusNodes().size() == 2) + .collect(Collectors.toList()); + context.hChains = chainNodeBusesWithFlatCells(context, candidateFlatCell); + buildBusToBelongings(context); + buildConnexClusters(context); + organizeClusters(context); + } + + private List identifyStructuringCells(Context context) { + List structuringInternCells + = context.graph.getCells().stream() + .filter(cell -> cell.getType() == Cell.CellType.INTERN) + .map(InternCell.class::cast) + .collect(Collectors.toList()); + + structuringInternCells.forEach(c -> { + if (c.isUniLeg()) { + addBusNodeSet(c.getSideBusNodes(Side.UNDEFINED), context); + } else { + addBusNodeSet(c.getSideBusNodes(Side.LEFT), context); + addBusNodeSet(c.getSideBusNodes(Side.RIGHT), context); + } + }); + + List verticalCells = structuringInternCells.stream() + .filter(internCell -> + new VerticalBusConnectionPattern(context, internCell.getBusNodes()).isIncludedIn(context.vbcpToCells.keySet()) != null) + .collect(Collectors.toList()); + structuringInternCells.removeAll(verticalCells); + return structuringInternCells; + } + + private List chainNodeBusesWithFlatCells(Context context, List flatCells) { + Map> bus2flatCells = new HashMap<>(); + flatCells.forEach(cell -> + cell.getBusNodes().forEach(busNode -> { + bus2flatCells.putIfAbsent(busNode, new ArrayList<>()); + bus2flatCells.get(busNode).add(cell); + })); + + Map> bus2vbcpBus = new HashMap<>(); + context.vbcpToCells.keySet() + .forEach(vbcp -> { + vbcp.getBusNodeSet().forEach(bus -> { + bus2vbcpBus.putIfAbsent(bus, new HashSet<>()); + bus2vbcpBus.get(bus).addAll(vbcp.getBusNodeSet()); + }); + }); + + List chains = new ArrayList<>(); + + List busConnectedToFlatCell = bus2flatCells.keySet().stream() + .sorted(Comparator.comparingInt(context.nodeToNb::get)) + .sorted(Comparator.comparingInt(bus -> bus2flatCells.get(bus).size())) + .collect(Collectors.toList()); + //this sorting is to ensure that in most cases (non circular chain) the first bus of a chain is connected to + // a single flat cell and constitutes one extremity of the chain. + + Set remainingBus = new HashSet<>(context.graph.getNodeBuses()); + remainingBus.removeAll(busConnectedToFlatCell); + + while (!busConnectedToFlatCell.isEmpty()) { + BusNode bus = busConnectedToFlatCell.get(0); + HorizontalChain hChain = new HorizontalChain(); + rBuildHChain(hChain, bus, busConnectedToFlatCell, new ArrayList<>(busConnectedToFlatCell), bus2vbcpBus, bus2flatCells); + chains.add(hChain); + } + for (BusNode bus : remainingBus) { + HorizontalChain chain = new HorizontalChain(bus); + chains.add(chain); + } + + return chains.stream() + .sorted(Comparator.comparingInt(hchain -> -hchain.busNodes.size())) + .collect(Collectors.toList()); + } + + private void rBuildHChain(HorizontalChain hChain, + BusNode bus, + List busConnectedToFlatCell, + List busOnRight, + Map> bus2vbcpBus, + Map> bus2flatCells) { + hChain.busNodes.add(bus); + busConnectedToFlatCell.remove(bus); + busOnRight.removeAll(bus2vbcpBus.get(bus)); + for (InternCell cell : bus2flatCells.get(bus)) { + BusNode otherBus = cell.getBusNodes() + .stream() + .filter(busNode -> busOnRight.contains(busNode)).findAny().orElse(null); + if (otherBus != null && busConnectedToFlatCell.contains(otherBus)) { + rBuildHChain(hChain, otherBus, busConnectedToFlatCell, busOnRight, bus2vbcpBus, bus2flatCells); + } + } + + } + + private void buildBusToBelongings(Context context) { + context.vbcpToCells.keySet().forEach(vbcp -> + vbcp.busNodeSet.forEach(busNode -> { + context.busToBelonging.putIfAbsent(busNode, new NodeBelonging(busNode)); + context.busToBelonging.get(busNode).vbcps.add(vbcp); + })); + context.hChains.forEach(hChain -> hChain.busNodes.forEach(busNode -> { + context.busToBelonging.putIfAbsent(busNode, new NodeBelonging(busNode)); + context.busToBelonging.get(busNode).hChain = hChain; + })); + } + + private void buildConnexClusters(Context context) { + List remainingBuses = context.graph.getNodeBuses(); + while (!remainingBuses.isEmpty()) { + context.connectedClusters.add(new ConnectedCluster(context, remainingBuses)); + } + } + + private void organizeClusters(Context context) { + int firstStructuralPosition = 0; + int firstFeederOrder = 1; + context.graph.getNodeBuses().forEach(busNode -> busNode.setStructuralPosition(null)); + for (ConnectedCluster cc : context.connectedClusters) { + firstStructuralPosition = cc.setStructuralPositions(firstStructuralPosition + 1); + firstFeederOrder = cc.setCellOrders(firstFeederOrder); + } + } + + private void newStructuralPosition(Context context) { + int i = 1; + for (VerticalBusConnectionPattern vbcp : context.vbcpToCells.keySet()) { + int j = 1; + for (BusNode busNode : vbcp.getBusNodeSet()) { + if (busNode.getStructuralPosition() == null) { + busNode.setStructuralPosition(new Position(i, j)); + } + j++; + } + i++; + } + for (BusNode bus : context.graph.getNodeBuses()) { + if (bus.getStructuralPosition() == null) { + bus.setStructuralPosition(new Position(i, 1)); + i++; + } + } + } + + private class VerticalBusConnectionPattern { + private Set busNodeSet; + + VerticalBusConnectionPattern(Context context, List busNodees) { + busNodeSet = new TreeSet<>(Comparator.comparingInt(context.nodeToNb::get)); + busNodeSet.addAll(busNodees); + } + + @Override + public boolean equals(Object o) { + if (o instanceof VerticalBusConnectionPattern) { + return busNodeSet.equals(((VerticalBusConnectionPattern) o).busNodeSet); + } else { + return false; + } + } + + @Override + public int hashCode() { + return busNodeSet.hashCode(); + } + + boolean isIncludedIn(VerticalBusConnectionPattern vbcp2) { + Iterator it1 = busNodeSet.iterator(); + Iterator it2 = vbcp2.getBusNodeSet().iterator(); + boolean match = true; + while (it1.hasNext() && match) { + BusNode n1 = it1.next(); + match = false; + while (it2.hasNext() && !match) { + BusNode n2 = it2.next(); + match = n2 == n1; + } + } + return match; + } + + VerticalBusConnectionPattern isIncludedIn(Set busConnectionPatterns) { + for (VerticalBusConnectionPattern candidateBCPIncluser : busConnectionPatterns) { + if (isIncludedIn(candidateBCPIncluser)) { + return candidateBCPIncluser; + } + } + return null; + } + + Set getBusNodeSet() { + return busNodeSet; + } + } + + private class HorizontalChain { + List busNodes; + int vbcpOrder; + int v; + + HorizontalChain() { + busNodes = new ArrayList<>(); + v = 0; + } + + HorizontalChain(BusNode busNode) { + this(); + busNodes.add(busNode); + } + + int getPosition(BusNode bus) { + return busNodes.indexOf(bus); + } + + int getDeltaPosition(BusNode bus1, BusNode bus2) { + return getPosition(bus1) - getPosition(bus2); + } + + void alignTo(HorizontalChain other) { + List intersection = new ArrayList<>(other.busNodes); + intersection.retainAll(busNodes); + for (int i = 0; i < intersection.size(); i++) { + for (int j = i + 1; j < intersection.size(); j++) { + BusNode bus1 = intersection.get(i); + BusNode bus2 = intersection.get(j); + if (getDeltaPosition(bus1, bus2) * other.getDeltaPosition(bus1, bus2) < 0) { + Collections.reverse(busNodes); + return; + } + } + } + } + } + + private class NodeBelonging { + BusNode busNode; + List vbcps; + HorizontalChain hChain; + + NodeBelonging(BusNode bus) { + busNode = bus; + vbcps = new ArrayList<>(); + } + } + + /** + * A connectedCluster bundle busNodes that are connected through a path of HChains and VerticalBusConnectionPatterns + */ + private class ConnectedCluster { + Set buses; + List vbcps; + List hChains; + Context context; + + ConnectedCluster(Context context, List remainingBuses) { + this.context = context; + buses = new HashSet<>(); + rBuild(remainingBuses.get(0), remainingBuses); + vbcps = buses.stream().flatMap(bus -> bus.vbcps.stream()).distinct().collect(Collectors.toList()); + hChains = buses.stream().map(bus -> bus.hChain).distinct().collect(Collectors.toList()); + alignChains(); + sortVbcp(); + organizeHChainsVertically(); + } + + private void rBuild(BusNode startingNode, List remainingBuses) { + if (remainingBuses.contains(startingNode)) { + buses.add(context.busToBelonging.get(startingNode)); + remainingBuses.remove(startingNode); + NodeBelonging nodeBelonging = context.busToBelonging.get(startingNode); + List busToHandle = nodeBelonging.vbcps.stream() + .flatMap(vbcp -> vbcp.busNodeSet.stream()).collect(Collectors.toList()); + busToHandle.addAll(nodeBelonging.hChain.busNodes); + busToHandle.forEach(busNode -> rBuild(busNode, remainingBuses)); + } + } + + private void alignChains() { + for (int i = 0; i < hChains.size(); i++) { + for (int j = i + 1; j < hChains.size(); j++) { + hChains.get(j).alignTo(hChains.get(i)); + } + } + } + + private List gethChainsFromVbcp(VerticalBusConnectionPattern vbcp) { + return vbcp.busNodeSet.stream() + .map(context.busToBelonging::get) + .map(nodeBelonging -> nodeBelonging.hChain).collect(Collectors.toList()); + } + + private BusNode intersectionNode(Collection busNodes1, Collection busNodes2) { + List intersectionList = new ArrayList<>(busNodes1); + intersectionList.retainAll(busNodes2); + if (intersectionList.isEmpty()) { + return null; + } else { + return intersectionList.get(0); + } + } + + private void sortVbcp() { + if (vbcps.isEmpty()) { + return; + } + List remainingVbcp = new ArrayList<>(vbcps); + List sortedVbcp = new ArrayList<>(); + sortedVbcp.add(remainingVbcp.get(0)); + remainingVbcp.remove(0); + int previousSize; + while (!remainingVbcp.isEmpty()) { + previousSize = remainingVbcp.size(); + sortWhenObviousComparisonsExist(remainingVbcp, sortedVbcp); + if (previousSize == remainingVbcp.size()) { //plan B ! + unblockSortingWithSimilarityCriteria(remainingVbcp, sortedVbcp); + } + } + vbcps = sortedVbcp; + } + + private void sortWhenObviousComparisonsExist(List remainingVbcp, + List sortedVbcp) { + for (VerticalBusConnectionPattern vbcp : remainingVbcp) { + if (tryToInsertVbcp(vbcp, sortedVbcp)) { + remainingVbcp.remove(vbcp); + break; + } + } + } + + private boolean tryToInsertVbcp(VerticalBusConnectionPattern vbcp, + List sortedList) { + int compare = 0; + for (VerticalBusConnectionPattern iterVbcp : sortedList) { + compare = compareHVbcp(vbcp, iterVbcp); + if (compare < 0) { + int position = sortedList.indexOf(iterVbcp); + sortedList.add(position, vbcp); + return true; + } + } + if (compare > 0) { + sortedList.add(vbcp); + return true; + } + return false; + } + + // don't use it as a comparator : if 2 vbcp are not comparable, the result is 0, + // but the to vbcp could be far from one another -> necessary to have a dedicated sorting function -> sortVbcp() + private int compareHVbcp(VerticalBusConnectionPattern vbcp1, VerticalBusConnectionPattern vbcp2) { + List commonChains = intersectChains(vbcp1, vbcp2); + if (commonChains.isEmpty()) { + return 0; + } + for (HorizontalChain chain : commonChains) { + int index1 = chain.getPosition(intersectionNode(chain.busNodes, vbcp1.busNodeSet)); + int index2 = chain.getPosition(intersectionNode(chain.busNodes, vbcp2.busNodeSet)); + if (index1 != -1 && index2 != -1 && index1 != index2) { + return index1 - index2; + } + } + return 0; + } + + private List intersectChains(VerticalBusConnectionPattern vbcp1, VerticalBusConnectionPattern vbcp2) { + List commonChains = gethChainsFromVbcp(vbcp1); + commonChains.retainAll(gethChainsFromVbcp(vbcp2)); + return commonChains; + } + + private void unblockSortingWithSimilarityCriteria(List remainingVbcps, + List sortedVbcps) { + VerticalBusConnectionPattern matchingRemainingVbcp = remainingVbcps.get(0); + VerticalBusConnectionPattern matchingSortedVbcp = sortedVbcps.get(0); + int maxIntersection = 0; + for (VerticalBusConnectionPattern sortedVbcp : sortedVbcps) { + for (VerticalBusConnectionPattern remainingVbcp : remainingVbcps) { + int intersectionSize = intersectChains(sortedVbcp, remainingVbcp).size(); + if (intersectionSize > maxIntersection) { + maxIntersection = intersectionSize; + matchingRemainingVbcp = remainingVbcp; + matchingSortedVbcp = sortedVbcp; + } + } + } + sortedVbcps.add(sortedVbcps.indexOf(matchingSortedVbcp) + 1, matchingRemainingVbcp); + remainingVbcps.remove(matchingRemainingVbcp); + } + + private void organizeHChainsVertically() { + int vbcpOrder = 0; + for (VerticalBusConnectionPattern vbcp : vbcps) { + Set vBooked = new TreeSet<>(Comparator.comparingInt(Integer::intValue)); + vbcp.busNodeSet.forEach(bus -> vBooked.add(context.busToBelonging.get(bus).hChain.v)); + for (BusNode bus : vbcp.busNodeSet) { + HorizontalChain chain = context.busToBelonging.get(bus).hChain; + if (chain.v == 0) { + int v = firstAvailableIndex(vBooked); + chain.v = v; + chain.vbcpOrder = vbcpOrder; + vBooked.add(v); + } + } + vbcpOrder++; + } + } + + private int firstAvailableIndex(Set integerSet) { + if (integerSet.isEmpty() + || integerSet.size() == 1 && integerSet.iterator().next() == 0) { + return 1; + } + int h = 1; + if (integerSet.iterator().next() == 0) { + h = 0; + } + for (int i : integerSet) { + if (i == h) { + h++; + } else { + return h; + } + } + return h; + } + + int setStructuralPositions(int firstStructuralHPosition) { + int maxH = firstStructuralHPosition; + for (HorizontalChain chain : hChains) { + int newH = chain.vbcpOrder + firstStructuralHPosition; + for (BusNode bus : chain.busNodes) { + if (bus.getStructuralPosition() == null) { + bus.setStructuralPosition(new Position(newH++, chain.v)); + } + } + maxH = Math.max(maxH, newH); + } + return maxH; + } + + int setCellOrders(int firstFeederOrder) { + int feederPosition = firstFeederOrder; + int cellPos = 0; + for (VerticalBusConnectionPattern vbcp : vbcps) { + for (ExternCell cell : context.vbcpToCells.get(vbcp)) { + cell.setDirection(cellPos % 2 == 0 ? BusCell.Direction.TOP : BusCell.Direction.BOTTOM); + cell.setOrder(cellPos); + cellPos++; + for (FeederNode feederNode : cell.getNodes().stream() + .filter(n -> n.getType() == Node.NodeType.FEEDER) + .map(FeederNode.class::cast).collect(Collectors.toList())) { + feederNode.setOrder(feederPosition); + feederPosition++; + } + } + } + return feederPosition; + } + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFromExtension.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFromExtension.java new file mode 100644 index 000000000..3fad87df9 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionFromExtension.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class PositionFromExtension implements PositionFinder { + + private static final Logger LOGGER = LoggerFactory.getLogger(PositionFromExtension.class); + + private static final BusCell.Direction DEFAULTDIRECTION = BusCell.Direction.TOP; + + /** + * Builds the layout of the bus nodes, and organises cells (order and directions) + */ + @Override + public void buildLayout(Graph graph) { + gatherLayoutExtensionInformation(graph); + List problematicCells = graph.getCells().stream() + .filter(cell -> cell.getType().equals(Cell.CellType.EXTERN)) + .map(ExternCell.class::cast) + .filter(cell -> cell.getOrder() == -1).collect(Collectors.toList()); + if (!problematicCells.isEmpty()) { + LOGGER.info("Unable to build the layout only with Extension\nproblematic cells :"); + problematicCells.forEach(cell -> LOGGER + .info("Cell Nb : {}, Order : {}, Type : {}", + cell.getNumber(), + cell.getOrder(), + cell.getType())); + return; + } + graph.setMaxBusPosition(); + forceSameOrientationForShuntedCell(graph); + } + + private void gatherLayoutExtensionInformation(Graph graph) { + graph.getNodes().stream() + .filter(node -> node.getType() == Node.NodeType.FEEDER) + .map(FeederNode.class::cast) + .forEach(feederNode -> { + ExternCell cell = (ExternCell) feederNode.getCell(); + cell.setDirection( + feederNode.getDirection() == BusCell.Direction.UNDEFINED ? DEFAULTDIRECTION : feederNode.getDirection()); + cell.setOrder(feederNode.getOrder()); + }); + graph.getCells().stream().filter(cell -> cell.getType() == Cell.CellType.EXTERN).map(ExternCell.class::cast) + .forEach(ExternCell::orderFromFeederOrders); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayout.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayout.java new file mode 100644 index 000000000..b42ed7278 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayout.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.Cell; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.Node; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class PositionVoltageLevelLayout implements VoltageLevelLayout { + + private static final Logger LOGGER = LoggerFactory.getLogger(PositionVoltageLevelLayout.class); + + private final Graph graph; + + public PositionVoltageLevelLayout(Graph graph) { + this.graph = Objects.requireNonNull(graph); + } + + /** + * Calculate real coordinate of busNode and blocks connected to busbar + */ + @Override + public void run(LayoutParameters layoutParam) { + LOGGER.info("Running voltage level layout"); + calculateBusNodeCoord(graph, layoutParam); + calculateCellCoord(graph, layoutParam); + graph.getNodes().stream() + .filter(node -> node.getType() != Node.NodeType.BUS) + .forEach(Node::finalizeCoord); + } + + private void calculateBusNodeCoord(Graph graph, LayoutParameters layoutParam) { + graph.getNodeBuses().forEach(nb -> nb.calculateCoord(layoutParam)); + } + + private void calculateCellCoord(Graph graph, LayoutParameters layoutParam) { + graph.getCells().stream() + .filter(cell -> cell.getType() == Cell.CellType.EXTERN + || cell.getType() == Cell.CellType.INTERN) + .forEach(cell -> cell.calculateCoord(layoutParam)); + graph.getCells().stream() + .filter(cell -> cell.getType() == Cell.CellType.SHUNT) + .forEach(cell -> cell.calculateCoord(layoutParam)); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayoutFactory.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayoutFactory.java new file mode 100644 index 000000000..9dca1f723 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/PositionVoltageLevelLayoutFactory.java @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.Graph; + +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class PositionVoltageLevelLayoutFactory implements VoltageLevelLayoutFactory { + + private final PositionFinder positionFinder; + + private boolean feederStacked = true; + + private boolean removeUnnecessaryFictitiousNodes = true; + + private boolean substituteSingularFictitiousByFeederNode = true; + + public PositionVoltageLevelLayoutFactory() { + this(new PositionFromExtension()); + } + + public PositionVoltageLevelLayoutFactory(PositionFinder positionFinder) { + this.positionFinder = Objects.requireNonNull(positionFinder); + } + + public boolean isFeederStacked() { + return feederStacked; + } + + public PositionVoltageLevelLayoutFactory setFeederStacked(boolean feederStacked) { + this.feederStacked = feederStacked; + return this; + } + + public boolean isRemoveUnnecessaryFictitiousNodes() { + return removeUnnecessaryFictitiousNodes; + } + + public PositionVoltageLevelLayoutFactory setRemoveUnnecessaryFictitiousNodes(boolean removeUnnecessaryFictitiousNodes) { + this.removeUnnecessaryFictitiousNodes = removeUnnecessaryFictitiousNodes; + return this; + } + + public boolean isSubstituteSingularFictitiousByFeederNode() { + return substituteSingularFictitiousByFeederNode; + } + + public PositionVoltageLevelLayoutFactory setSubstituteSingularFictitiousByFeederNode(boolean substituteSingularFictitiousByFeederNode) { + this.substituteSingularFictitiousByFeederNode = substituteSingularFictitiousByFeederNode; + return this; + } + + @Override + public VoltageLevelLayout create(Graph graph) { + // detect cells + new ImplicitCellDetector(removeUnnecessaryFictitiousNodes, substituteSingularFictitiousByFeederNode) + .detectCells(graph); + + // build blocks from cells + new BlockOrganizer(positionFinder, feederStacked).organize(graph); + + return new PositionVoltageLevelLayout(graph); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/RandomVoltageLevelLayout.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/RandomVoltageLevelLayout.java new file mode 100644 index 000000000..1b8d582f7 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/RandomVoltageLevelLayout.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.Node; +import com.powsybl.sld.model.BusNode; + +import java.util.Objects; +import java.util.Random; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class RandomVoltageLevelLayout implements VoltageLevelLayout { + + private final Graph graph; + + private final double width; + + private final double height; + + private final Random random; + + public RandomVoltageLevelLayout(Graph graph, double width, double height, Random random) { + this.graph = Objects.requireNonNull(graph); + this.width = width; + this.height = height; + this.random = Objects.requireNonNull(random); + } + + @Override + public void run(LayoutParameters layoutParam) { + for (Node node : graph.getNodes()) { + node.setX(random.nextDouble() * width); + node.setY(random.nextDouble() * height); + if (node instanceof BusNode) { + ((BusNode) node).setPxWidth(50); + } + } + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/RandomVoltageLevelLayoutFactory.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/RandomVoltageLevelLayoutFactory.java new file mode 100644 index 000000000..df21993a0 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/RandomVoltageLevelLayoutFactory.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.Graph; + +import java.util.Random; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class RandomVoltageLevelLayoutFactory implements VoltageLevelLayoutFactory { + + private final double width; + + private final double height; + + private final Random random = new Random(); + + public RandomVoltageLevelLayoutFactory(double width, double height) { + this.width = width; + this.height = height; + } + + @Override + public VoltageLevelLayout create(Graph graph) { + return new RandomVoltageLevelLayout(graph, width, height, random); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubSections.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubSections.java new file mode 100644 index 000000000..498756e64 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubSections.java @@ -0,0 +1,417 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nonnull; +import java.util.*; +import java.util.stream.Collectors; + +/** + * SubSections splits the horizontal organisation of the busBars to cope with the case parallelism is not respected + * This solves the case of a busbar spanning over many busbars at another vertical structural position. + * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +class SubSections { + + private Graph graph; + + private static final Logger LOGGER = LoggerFactory.getLogger(SubSections.class); + + private Map subsectionMap; + + private static final String STR_SIDE = "\t side "; + + SubSections(Graph graph) { + this.graph = graph; + subsectionMap = new TreeMap<>(); + } + + class ExternCellComparator implements Comparator { + public int compare(ExternCell extCell1, ExternCell extCell2) { + if (extCell1 == extCell2) { + return 0; + } + if (extCell1.getOrder() == extCell2.getOrder()) { + return Comparator.comparingInt(ExternCell::getNumber).compare(extCell1, extCell2); + } + return Comparator.comparingInt(ExternCell::getOrder).compare(extCell1, extCell2); + } + } + + class HorizontalSubSection { + private Set cells; + private Set externCells; + private Set internCells; + private Set busNodes; + private Map cellToSideMap; + private int order; + + HorizontalSubSection() { + cells = new HashSet<>(); + externCells = new TreeSet<>(new ExternCellComparator()); + + internCells = new HashSet<>(); + busNodes = new HashSet<>(); + cellToSideMap = new HashMap<>(); + order = -1; + } + + void add(BusCell busCell) { + cells.add(busCell); + if (busCell.getType() == Cell.CellType.EXTERN) { + externCells.add((ExternCell) busCell); + if (order == -1) { + order = ((ExternCell) busCell).getOrder(); + } + } + busNodes.addAll(busCell.getBusNodes()); + } + + void add(InternCell cell, Side side) { + cells.add(cell); + internCells.add(cell); + if (cellToSideMap.containsKey(cell) || side == Side.UNDEFINED) { + cellToSideMap.put(cell, Side.UNDEFINED); // vertical coupling + } else { + cellToSideMap.put(cell, + side == Side.RIGHT ? Side.LEFT : Side.RIGHT); //inversion, the left leg of an interncell, is on the right side of the subsection + } + busNodes.addAll(cell.getBusNodes()); + } + + void merge(HorizontalSubSection hss) { + cells.addAll(hss.cells); + externCells.addAll(hss.externCells); + internCells.addAll(hss.internCells); + busNodes.addAll(hss.busNodes); + cellToSideMap.putAll(hss.cellToSideMap); + } + + @Override + public String toString() { + StringBuilder strBd = new StringBuilder(); + + Set leftCells = getSideInternCells(Side.LEFT); + if (!leftCells.isEmpty()) { + strBd.append("internCells Left: ").append(leftCells.size()).append("\n"); + leftCells.forEach(cell -> strBd.append(STR_SIDE).append(" ").append(cell.toString()).append("\n")); + } + + if (!externCells.isEmpty()) { + strBd.append("externCells: ").append(externCells.size()).append("\n"); + externCells.forEach(cell -> strBd.append("\t").append(cell.toString()).append("\n")); + } + + Set undefinedCells = getSideInternCells(Side.UNDEFINED); + if (!undefinedCells.isEmpty()) { + strBd.append("undefined internCells: ").append(undefinedCells.size()).append("\n"); + undefinedCells.forEach(cell -> strBd.append(STR_SIDE).append(" ").append(cell.toString()).append("\n")); + } + + Set rightCells = getSideInternCells(Side.RIGHT); + if (!rightCells.isEmpty()) { + strBd.append("internCells Right: ").append(rightCells.size()).append("\n"); + rightCells.forEach(cell -> strBd.append(STR_SIDE).append(" ").append(cell).append("\n")); + } + + strBd.append("busNodes: ").append(busNodes.size()).append("\n"); + busNodes.forEach(node -> strBd.append("\t").append(node.toString()).append("\n")); + + return strBd.toString(); + } + + Set getBusNodes() { + return new HashSet<>(busNodes); + } + + Set getExternCells() { + TreeSet externCellsCopy = new TreeSet<>(new ExternCellComparator()); + externCellsCopy.addAll(this.externCells); + return externCellsCopy; + } + + Set getCells() { + return new HashSet<>(cells); + } + + Set getInternCells() { + return new HashSet<>(internCells); + } + + Set getSideInternCells(Side side) { + return internCells.stream().filter(cell -> cellToSideMap.get(cell) == side) + .collect(Collectors.toSet()); + } + + Map getCellToSideMap() { + return cellToSideMap; + } + + void setSide(InternCell c, Side side) { + cellToSideMap.put(c, side); + } + + } + + class SubSectionIndexes implements Comparable { + private int size; + private int[] indexes; + private int order; + + SubSectionIndexes(int size) { + this.size = size; + indexes = new int[size]; + order = -1; + } + + void setIndexI(int i, int val) { + indexes[i] = val; + } + + void updateOrder(int order) { + this.order = Math.max(this.order, order); + } + + int[] getIndexes() { + return indexes.clone(); + } + + boolean asSameNonZeroIndexes(SubSectionIndexes ssI) { + for (int i = 0; i < size; i++) { + int index = ssI.getIndexes()[i]; + if (index != 0 && index != indexes[i]) { + return false; + } + } + return true; + } + + @Override + public boolean equals(Object ss) { + if (ss instanceof SubSectionIndexes) { + return Arrays.equals(((SubSectionIndexes) ss).getIndexes(), this.indexes); + } else { + return false; + } + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + @Override + public String toString() { + return Arrays.toString(indexes); + } + + @Override + public int compareTo(@Nonnull SubSectionIndexes o) { + boolean hasBoth0 = false; + for (int i = 0; i < size; i++) { + if (indexes[i] != 0 && o.getIndexes()[i] != 0) { + int index = o.getIndexes()[i]; + if (indexes[i] != index) { + return indexes[i] - index; + } + } else { + hasBoth0 = true; + } + } + if (hasBoth0) { + return notObviousComp(o.getIndexes()); + } + return 0; + } + + private int notObviousComp(int[] indexes2) { + int compMax = Arrays.stream(indexes).max().orElse(0) - Arrays.stream(indexes2).max().orElse(0); + if (compMax != 0) { + return compMax; + } + for (int i = 0; i < size; i++) { + int index = indexes2[i]; + if (indexes[i] != index) { + return indexes[i] - index; + } + } + return 0; + } + + SubSectionIndexes merge(SubSectionIndexes ssi) { + SubSectionIndexes resSsi = new SubSectionIndexes(size); + for (int i = 0; i < size; i++) { + if (ssi.indexes[i] != 0 && indexes[i] != 0) { + if (ssi.indexes[i] != indexes[i]) { + return new SubSectionIndexes(0); + } else { + resSsi.indexes[i] = indexes[i]; + } + } else { + resSsi.indexes[i] = Math.max(ssi.indexes[i], indexes[i]); + } + } + resSsi.order = Math.max(ssi.order, order); + return resSsi; + } + + } + + void handleSpanningBusBar() { + buildSubSections(); + checkInternCellOrientation(); + if (!checkCellOrderConsistencyWithSubsSections()) { + LOGGER.warn("*************** Cells order not consistent with Subsections order"); + } + } + + private void checkInternCellOrientation() { + Map> cellToIndex = new HashMap<>(); + + subsectionMap.forEach((ssI, ssh) -> ssh.getInternCells() + .forEach(c -> { + cellToIndex.putIfAbsent(c, new ArrayList<>()); + cellToIndex.get(c).add(ssI); + })); + + cellToIndex.forEach((c, ssiList) -> { + if (ssiList.size() == 2) { + HorizontalSubSection leftHss = subsectionMap.get(ssiList.get(0)); + HorizontalSubSection rightHss = subsectionMap.get(ssiList.get(1)); + if (leftHss.getSideInternCells(Side.LEFT).contains(c) + && rightHss.getSideInternCells(Side.RIGHT).contains(c)) { + c.reverseCell(); + leftHss.setSide(c, Side.RIGHT); + rightHss.setSide(c, Side.LEFT); + } + } + }); + } + + private boolean verticalInternCell(InternCell cell) { + return cell.isUniLeg() || cell.getBusNodes().stream() + .map(bus -> bus.getStructuralPosition().getH()) + .distinct().count() == 1; + } + + private void buildSubSections() { + graph.getCells().stream().filter(cell -> cell.getType() == Cell.CellType.EXTERN) + .map(ExternCell.class::cast) + .forEach(cell -> allocateCellToSubsection(cell, cell.getBusNodes(), Side.UNDEFINED)); + + Set internCells = graph.getCells().stream() + .filter(cell -> cell.getType() == Cell.CellType.INTERN) + .map(InternCell.class::cast) + .collect(Collectors.toSet()); + + Set verticalInternCells = internCells.stream().filter(this::verticalInternCell) + .collect(Collectors.toSet()); + verticalInternCells.forEach(cell -> allocateCellToSubsection(cell, cell.getBusNodes(), Side.UNDEFINED)); + + internCells.removeAll(verticalInternCells); + + internCells.stream() + .filter(cell -> cell.getType() == Cell.CellType.INTERN) + .forEach(internCell -> { + allocateCellToSubsection(internCell, internCell.getSideBusNodes(Side.LEFT), Side.LEFT); + allocateCellToSubsection(internCell, internCell.getSideBusNodes(Side.RIGHT), Side.RIGHT); + }); + mergeSimilarSubstations(); + } + + private void mergeSimilarSubstations() { + boolean change = true; + while (change) { + change = false; + List ssiList = new ArrayList<>(subsectionMap.keySet()); + for (int i = 0; i < ssiList.size() && !change; i++) { + SubSectionIndexes ssi1 = ssiList.get(i); + for (int j = i + 1; j < ssiList.size() && !change; j++) { + SubSectionIndexes ssi2 = ssiList.get(j); + SubSectionIndexes newSSI = ssi1.merge(ssi2); + if (newSSI.size != 0) { + change = true; + HorizontalSubSection hss1 = subsectionMap.get(ssi1); + HorizontalSubSection hss2 = subsectionMap.get(ssi2); + subsectionMap.remove(ssi1); + subsectionMap.remove(ssi2); + hss1.merge(hss2); + if (subsectionMap.containsKey(newSSI)) { + hss1.merge(subsectionMap.get(newSSI)); + } + subsectionMap.put(newSSI, hss1); + } + } + } + } + } + + private void allocateCellToSubsection(BusCell busCell, List busNodes, Side side) { + SubSectionIndexes indexes = new SubSectionIndexes(graph.getMaxBusStructuralPosition().getV()); + busNodes.stream().map(BusNode::getStructuralPosition).collect(Collectors.toList()) + .forEach(position -> indexes.setIndexI(position.getV() - 1, position.getH())); + + if (side == Side.UNDEFINED) { + subsectionMap.putIfAbsent(indexes, new HorizontalSubSection()); + if (busCell.getType() == Cell.CellType.INTERN) { + subsectionMap.get(indexes).add((InternCell) busCell, side); + } else { + subsectionMap.get(indexes).add(busCell); + indexes.updateOrder(((ExternCell) busCell).getOrder()); + } + } else { + Set candidateSubsectionIndexes; + candidateSubsectionIndexes = subsectionMap.keySet().stream() + .filter(ssi -> ssi.asSameNonZeroIndexes(indexes)) + .collect(Collectors.toSet()); + if (candidateSubsectionIndexes.isEmpty()) { + subsectionMap.put(indexes, new HorizontalSubSection()); + candidateSubsectionIndexes.add(indexes); + } + SubSectionIndexes ssI; + if (side == Side.LEFT) { + ssI = Collections.max(candidateSubsectionIndexes); + } else { + ssI = Collections.min(candidateSubsectionIndexes); + } + subsectionMap.get(ssI).add((InternCell) busCell, side); + } + } + + private boolean checkCellOrderConsistencyWithSubsSections() { + int previousMax = 0; + boolean checkOK = true; + for (Map.Entry entry : subsectionMap.entrySet()) { + Set externCells = entry.getValue().getExternCells(); + int minOrder = externCells.stream().mapToInt(ExternCell::getOrder).max().orElse(previousMax); + checkOK &= minOrder >= previousMax; + previousMax = externCells.stream().mapToInt(ExternCell::getOrder).max().orElse(previousMax); + } + return checkOK; + } + + @Override + public String toString() { + StringBuilder stBdr = new StringBuilder(); + getSubsectionMap().forEach((indexes, subsection) -> { + stBdr.append(Arrays.toString(indexes.getIndexes())).append(":\n"); + stBdr.append(subsection.toString()).append("\n"); + }); + return stBdr.toString(); + } + + Map getSubsectionMap() { + return new TreeMap<>(subsectionMap); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubstationLayout.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubstationLayout.java new file mode 100644 index 000000000..6290b75c6 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubstationLayout.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +/** + * @author Franck Lecuyer + */ +public interface SubstationLayout { + + /** + * Calculate real coordinates of nodes in the substation graph + */ + void run(LayoutParameters layoutParameters); + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubstationLayoutFactory.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubstationLayoutFactory.java new file mode 100644 index 000000000..b6c851461 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/SubstationLayoutFactory.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.SubstationGraph; + +/** + * @author Franck Lecuyer + */ +public interface SubstationLayoutFactory { + + SubstationLayout create(SubstationGraph graph, VoltageLevelLayoutFactory vLayoutFactory); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VerticalSubstationLayout.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VerticalSubstationLayout.java new file mode 100644 index 000000000..9bd999546 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VerticalSubstationLayout.java @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * @author Franck Lecuyer + */ +public class VerticalSubstationLayout extends AbstractSubstationLayout { + + public VerticalSubstationLayout(SubstationGraph graph, VoltageLevelLayoutFactory vLayoutFactory) { + super(graph, vLayoutFactory); + } + + /** + * Calculate relative coordinate of voltageLevels in the substation + */ + @Override + protected Coord calculateCoordVoltageLevel(LayoutParameters layoutParam, Graph vlGraph) { + int maxV = vlGraph.getNodeBuses().stream() + .mapToInt(nodeBus -> nodeBus.getPosition().getV() + nodeBus.getPosition().getVSpan()) + .max().orElse(0); + + double x = 0; + double y = layoutParam.getInitialYBus() + layoutParam.getStackHeight() + + layoutParam.getExternCellHeight() + layoutParam.getVerticalSpaceBus() * (maxV + 2); + + return new Coord(x, y); + } + + /* + * Calculate polyline points of a snakeLine in the substation graph + */ + @Override + protected List calculatePolylineSnakeLine(LayoutParameters layoutParam, + Edge edge, + Map nbSnakeLinesTopBottom, + Map nbSnakeLinesLeftRight, + Map nbSnakeLinesBetween, + Map nbSnakeLinesBottomVL, + Map nbSnakeLinesTopVL) { + Node node1 = edge.getNode1(); + Node node2 = edge.getNode2(); + + BusCell.Direction dNode1 = getNodeDirection(node1, 1); + BusCell.Direction dNode2 = getNodeDirection(node2, 2); + + double xMinGraph; + + if (node1.getGraph().getX() < node2.getGraph().getX()) { + xMinGraph = node1.getGraph().getX(); + } else { + xMinGraph = node2.getGraph().getX(); + } + + double x1 = node1.getX(); + double y1 = node1.getY(); + double x2 = node2.getX(); + double y2 = node2.getY(); + + int maxH1 = node1.getGraph().getNodeBuses().stream() + .mapToInt(nodeBus -> nodeBus.getPosition().getH() + nodeBus.getPosition().getHSpan()) + .max().orElse(0); + int maxH2 = node2.getGraph().getNodeBuses().stream() + .mapToInt(nodeBus -> nodeBus.getPosition().getH() + nodeBus.getPosition().getHSpan()) + .max().orElse(0); + double maxH = layoutParam.getTranslateX() + + nbSnakeLinesLeftRight.get(Side.LEFT) * layoutParam.getHorizontalSnakeLinePadding() + + layoutParam.getInitialXBus() + + (Math.max(maxH1, maxH2)) * layoutParam.getCellWidth(); + + List pol = new ArrayList<>(); + switch (dNode1) { + case BOTTOM: + if (dNode2 == BusCell.Direction.BOTTOM) { // BOTTOM to BOTTOM + nbSnakeLinesBottomVL.compute(node1.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesBottomVL.compute(node2.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesLeftRight.compute(Side.RIGHT, (k, v) -> v + 1); + double decal1V = nbSnakeLinesBottomVL.get(node1.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double decal2V = nbSnakeLinesBottomVL.get(node2.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double xSnakeLine = maxH + nbSnakeLinesLeftRight.get(Side.RIGHT) * layoutParam.getHorizontalSnakeLinePadding(); + + pol.addAll(Arrays.asList(x1, y1, + x1, y1 + decal1V, + xSnakeLine, y1 + decal1V, + xSnakeLine, y2 + decal2V, + x2, y2 + decal2V, + x2, y2)); + } else { // BOTTOM to TOP + if (!graph.graphAdjacents(node1.getGraph(), node2.getGraph())) { + nbSnakeLinesBottomVL.compute(node1.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesTopVL.compute(node2.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesLeftRight.compute(Side.RIGHT, (k, v) -> v + 1); + double decal1V = nbSnakeLinesBottomVL.get(node1.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double decal2V = nbSnakeLinesTopVL.get(node2.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double xSnakeLine = maxH + nbSnakeLinesLeftRight.get(Side.RIGHT) * layoutParam.getHorizontalSnakeLinePadding(); + + pol.addAll(Arrays.asList(x1, y1, + x1, y1 + decal1V, + xSnakeLine, y1 + decal1V, + xSnakeLine, y2 - decal2V, + x2, y2 - decal2V, + x2, y2)); + } else { // node1 and node2 adjacent and node1 before node2 + nbSnakeLinesBottomVL.compute(node1.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesTopVL.compute(node2.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + double decal1V = nbSnakeLinesBottomVL.get(node1.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double decal2V = nbSnakeLinesTopVL.get(node2.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double ySnakeLine = Math.max(y1 + decal1V, y2 - decal2V); + + pol.addAll(Arrays.asList(x1, y1, + x1, ySnakeLine, + x2, ySnakeLine, + x2, y2)); + } + } + break; + + case TOP: + if (dNode2 == BusCell.Direction.TOP) { // TOP to TOP + nbSnakeLinesTopVL.compute(node1.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesTopVL.compute(node2.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesLeftRight.compute(Side.LEFT, (k, v) -> v + 1); + double decal1V = nbSnakeLinesTopVL.get(node1.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double decal2V = nbSnakeLinesTopVL.get(node2.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double xSnakeLine = xMinGraph - nbSnakeLinesLeftRight.get(Side.LEFT) * layoutParam.getHorizontalSnakeLinePadding(); + + pol.addAll(Arrays.asList(x1, y1, + x1, y1 - decal1V, + xSnakeLine, y1 - decal1V, + xSnakeLine, y2 - decal2V, + x2, y2 - decal2V, + x2, y2)); + } else { // TOP to BOTTOM + if (!graph.graphAdjacents(node2.getGraph(), node1.getGraph())) { + nbSnakeLinesTopVL.compute(node1.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesBottomVL.compute(node2.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesLeftRight.compute(Side.LEFT, (k, v) -> v + 1); + double decal1V = nbSnakeLinesTopVL.get(node1.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double decal2V = nbSnakeLinesBottomVL.get(node2.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double xSnakeLine = xMinGraph - nbSnakeLinesLeftRight.get(Side.LEFT) * layoutParam.getHorizontalSnakeLinePadding(); + + pol.addAll(Arrays.asList(x1, y1, + x1, y1 - decal1V, + xSnakeLine, y1 - decal1V, + xSnakeLine, y2 + decal2V, + x2, y2 + decal2V, + x2, y2)); + } else { // node1 and node2 adjacent and node2 before node1 + nbSnakeLinesTopVL.compute(node1.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + nbSnakeLinesBottomVL.compute(node2.getGraph().getVoltageLevel().getId(), (k, v) -> v + 1); + double decal1V = nbSnakeLinesTopVL.get(node1.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double decal2V = nbSnakeLinesBottomVL.get(node2.getGraph().getVoltageLevel().getId()) * layoutParam.getVerticalSnakeLinePadding(); + double ySnakeLine = Math.max(y1 - decal1V, y2 + decal2V); + + pol.addAll(Arrays.asList(x1, y1, + x1, ySnakeLine, + x2, ySnakeLine, + x2, y2)); + } + } + break; + default: + } + return pol; + } + + @Override + protected double getHorizontalSubstationPadding(LayoutParameters layoutParameters) { + return 0; + } + + @Override + protected double getVerticalSubstationPadding(LayoutParameters layoutParameters) { + return layoutParameters.getVerticalSubstationPadding(); + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VerticalSubstationLayoutFactory.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VerticalSubstationLayoutFactory.java new file mode 100644 index 000000000..3492d136c --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VerticalSubstationLayoutFactory.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.SubstationGraph; + +/** + * @author Franck Lecuyer + */ +public class VerticalSubstationLayoutFactory implements SubstationLayoutFactory { + + @Override + public SubstationLayout create(SubstationGraph graph, VoltageLevelLayoutFactory vLayoutFactory) { + return new VerticalSubstationLayout(graph, vLayoutFactory); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayout.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayout.java new file mode 100644 index 000000000..1dbbbff5c --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayout.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface VoltageLevelLayout { + + /** + * Calculate real coordinate of busbar and blocks connected to busbar + */ + void run(LayoutParameters layoutParam); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayoutFactory.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayoutFactory.java new file mode 100644 index 000000000..49da9f7e6 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/layout/VoltageLevelLayoutFactory.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import com.powsybl.sld.model.Graph; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface VoltageLevelLayoutFactory { + + VoltageLevelLayout create(Graph graph); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedAnchorPoint.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedAnchorPoint.java new file mode 100644 index 000000000..7e1a3a8ee --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedAnchorPoint.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +@XmlAccessorType(XmlAccessType.FIELD) +public class AdaptedAnchorPoint { + + @XmlAttribute(name = "x") + private double x = 0; + + @XmlAttribute(name = "y") + private double y = 0; + + @XmlAttribute(name = "orientation") + private AnchorOrientation orientation; + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } + + public AnchorOrientation getOrientation() { + return orientation; + } + + public void setOrientation(AnchorOrientation orientation) { + this.orientation = orientation; + } + + @Override + public String toString() { + return "AnchorPoint(x=" + x + ", y=" + y + ", orientation=" + orientation + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedComponentMetadata.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedComponentMetadata.java new file mode 100644 index 000000000..58117e689 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedComponentMetadata.java @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +@XmlAccessorType(XmlAccessType.FIELD) +public class AdaptedComponentMetadata { + + @XmlAttribute(name = "type", required = true) + private String type; + + @XmlAttribute(name = "id") + private String id; + + @XmlElement(name = "anchorPoint") + private List anchorPoints = new ArrayList<>(); + + private ComponentSize size; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ComponentSize getSize() { + return size; + } + + public void setSize(ComponentSize size) { + this.size = size; + } + + public List getAnchorPoints() { + return anchorPoints; + } + + public void setAnchorPoints(List anchorPoints) { + this.anchorPoints = anchorPoints; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedComponentSize.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedComponentSize.java new file mode 100644 index 000000000..0774f3b40 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AdaptedComponentSize.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlAttribute; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +@XmlAccessorType(XmlAccessType.FIELD) +public class AdaptedComponentSize { + + @XmlAttribute(name = "width") + private double width = 0; + + @XmlAttribute(name = "height") + private double height = 0; + + public double getWidth() { + return width; + } + + public void setWidth(double width) { + this.width = width; + } + + public double getHeight() { + return height; + } + + public void setHeight(double height) { + this.height = height; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorOrientation.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorOrientation.java new file mode 100644 index 000000000..996aaf919 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorOrientation.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public enum AnchorOrientation { + VERTICAL, + HORIZONTAL, + NONE +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPoint.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPoint.java new file mode 100644 index 000000000..9a8c0a49e --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPoint.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +@XmlJavaTypeAdapter(AnchorPointAdapter.class) +public class AnchorPoint { + + private final double x; + + private final double y; + + private final AnchorOrientation orientation; + + /** + * Constructor + * + * @param x abscissa + * @param y ordinate + * @param orientation connection orientation + */ + @JsonCreator + public AnchorPoint(@JsonProperty("x") double x, @JsonProperty("y") double y, + @JsonProperty("orientation") AnchorOrientation orientation) { + this.x = x; + this.y = y; + this.orientation = Objects.requireNonNull(orientation); + } + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public AnchorOrientation getOrientation() { + return orientation; + } + + /** + * Rotate the anchorPoints + */ + public AnchorPoint rotate(Double rotationAngle) { + if (rotationAngle == 90.) { + switch (orientation) { + case VERTICAL: + return new AnchorPoint(y, x, AnchorOrientation.HORIZONTAL); + case HORIZONTAL: + return new AnchorPoint(y, x, AnchorOrientation.VERTICAL); + case NONE: + return this; + default: + throw new AssertionError("Unknown anchor orientation " + orientation); + } + } else if (rotationAngle == 180.) { + return new AnchorPoint(-x, -y, orientation); + } else { + return this; + } + } + + public AnchorPoint rotate() { + return rotate(90.); + } + + @Override + public String toString() { + return "AnchorPoint(x=" + x + ", y=" + y + ", orientation=" + orientation + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPointAdapter.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPointAdapter.java new file mode 100644 index 000000000..5c244ad45 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPointAdapter.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class AnchorPointAdapter extends XmlAdapter { + + @Override + public AnchorPoint unmarshal(AdaptedAnchorPoint adapted) { + return new AnchorPoint(adapted.getX(), adapted.getY(), adapted.getOrientation()); + } + + @Override + public AdaptedAnchorPoint marshal(AnchorPoint anchorPoint) { + AdaptedAnchorPoint adapted = new AdaptedAnchorPoint(); + adapted.setX(anchorPoint.getX()); + adapted.setY(anchorPoint.getY()); + adapted.setOrientation(anchorPoint.getOrientation()); + return adapted; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPointProvider.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPointProvider.java new file mode 100644 index 000000000..81e942e09 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/AnchorPointProvider.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface AnchorPointProvider { + + List getAnchorPoints(String type, String id); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/Component.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/Component.java new file mode 100644 index 000000000..565024327 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/Component.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class Component { + + private String fileName; + + private ComponentMetadata metadata; + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public ComponentMetadata getMetadata() { + return metadata; + } + + public void setMetadata(ComponentMetadata metadata) { + this.metadata = metadata; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentLibrary.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentLibrary.java new file mode 100644 index 000000000..b6407d8f5 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentLibrary.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import org.apache.batik.anim.dom.SVGOMDocument; + +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface ComponentLibrary { + + List getAnchorPoints(String type); + + SVGOMDocument getSvgDocument(String type); + + ComponentSize getSize(String type); + + String getStyleSheet(); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentMetadata.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentMetadata.java new file mode 100644 index 000000000..8d0997577 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentMetadata.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +@XmlJavaTypeAdapter(ComponentMetadataAdapter.class) +public class ComponentMetadata { + + private final String type; + + private final String id; + + private final List anchorPoints; + + private final ComponentSize size; + + @JsonCreator + public ComponentMetadata(@JsonProperty("type") String type, + @JsonProperty("id") String id, + @JsonProperty("anchorPoints") List anchorPoints, + @JsonProperty("size") ComponentSize size) { + this.type = Objects.requireNonNull(type); + this.id = id; + this.anchorPoints = Collections.unmodifiableList(Objects.requireNonNull(anchorPoints)); + this.size = Objects.requireNonNull(size); + } + + public String getType() { + return type; + } + + public String getId() { + return id; + } + + public ComponentSize getSize() { + return size; + } + + public List getAnchorPoints() { + return anchorPoints; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentMetadataAdapter.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentMetadataAdapter.java new file mode 100644 index 000000000..f0562415d --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentMetadataAdapter.java @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class ComponentMetadataAdapter extends XmlAdapter { + + @Override + public ComponentMetadata unmarshal(AdaptedComponentMetadata adapted) { + return new ComponentMetadata(adapted.getType(), adapted.getId(), adapted.getAnchorPoints(), adapted.getSize()); + } + + @Override + public AdaptedComponentMetadata marshal(ComponentMetadata componentMetadata) { + AdaptedComponentMetadata adapted = new AdaptedComponentMetadata(); + adapted.setType(componentMetadata.getType()); + adapted.setId(componentMetadata.getId()); + adapted.setSize(componentMetadata.getSize()); + adapted.setAnchorPoints(componentMetadata.getAnchorPoints()); + return adapted; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentSize.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentSize.java new file mode 100644 index 000000000..5ae986f2a --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentSize.java @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +@XmlJavaTypeAdapter(ComponentSizeAdapter.class) +public class ComponentSize { + + private final double width; + + private final double height; + + @JsonCreator + public ComponentSize(@JsonProperty("width") double width, @JsonProperty("height") double height) { + this.width = width; + this.height = height; + } + + public double getWidth() { + return width; + } + + public double getHeight() { + return height; + } + + @Override + public String toString() { + return "ComponentSize(width=" + width + ", height=" + height + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentSizeAdapter.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentSizeAdapter.java new file mode 100644 index 000000000..6c89a065c --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentSizeAdapter.java @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class ComponentSizeAdapter extends XmlAdapter { + + @Override + public ComponentSize unmarshal(AdaptedComponentSize adapted) { + return new ComponentSize(adapted.getWidth(), adapted.getHeight()); + } + + @Override + public AdaptedComponentSize marshal(ComponentSize size) { + AdaptedComponentSize adapted = new AdaptedComponentSize(); + adapted.setWidth(size.getWidth()); + adapted.setHeight(size.getHeight()); + return adapted; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentTypeName.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentTypeName.java new file mode 100644 index 000000000..508d8e279 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ComponentTypeName.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +/** + * @author Franck Lecuyer + */ +public final class ComponentTypeName { + public static final String ARROW = "ARROW"; + public static final String BUSBAR_SECTION = "BUSBAR_SECTION"; + public static final String BREAKER = "BREAKER"; + public static final String DISCONNECTOR = "DISCONNECTOR"; + public static final String GENERATOR = "GENERATOR"; + public static final String LINE = "LINE"; + public static final String LOAD = "LOAD"; + public static final String LOAD_BREAK_SWITCH = "LOAD_BREAK_SWITCH"; + public static final String NODE = "NODE"; + public static final String CAPACITOR = "CAPACITOR"; + public static final String INDUCTOR = "INDUCTOR"; + public static final String STATIC_VAR_COMPENSATOR = "STATIC_VAR_COMPENSATOR"; + public static final String TWO_WINDINGS_TRANSFORMER = "TWO_WINDINGS_TRANSFORMER"; + public static final String THREE_WINDINGS_TRANSFORMER = "THREE_WINDINGS_TRANSFORMER"; + public static final String VSC_CONVERTER_STATION = "VSC_CONVERTER_STATION"; + public static final String DANGLING_LINE = "DANGLING_LINE"; + public static final String PHASE_SHIFT_TRANSFORMER = "PHASE_SHIFT_TRANSFORMER"; + + private ComponentTypeName() { + throw new AssertionError(); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/Components.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/Components.java new file mode 100644 index 000000000..c7a28d032 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/Components.java @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import com.powsybl.commons.exceptions.UncheckedJaxbException; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +@XmlRootElement(name = "components") +public class Components { + + @XmlElement(name = "component") + private final List components = new ArrayList<>(); + + public List getComponents() { + return components; + } + + public static Components load(String directory) { + return load(Components.class.getResourceAsStream(directory + "/components.xml")); + } + + public static Components load(InputStream is) { + try { + JAXBContext jc = JAXBContext.newInstance(Components.class); + Unmarshaller unmarshaller = jc.createUnmarshaller(); + return (Components) unmarshaller.unmarshal(is); + } catch (JAXBException e) { + throw new UncheckedJaxbException(e); + } + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ResourcesComponentLibrary.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ResourcesComponentLibrary.java new file mode 100644 index 000000000..a13ec864f --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/library/ResourcesComponentLibrary.java @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import com.google.common.io.ByteStreams; +import com.powsybl.sld.svg.SVGLoaderToDocument; +import org.apache.batik.anim.dom.SVGOMDocument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class ResourcesComponentLibrary implements ComponentLibrary { + + private static final Logger LOGGER = LoggerFactory.getLogger(ResourcesComponentLibrary.class); + + private final Map svgDocuments = new HashMap<>(); + + private final Map components; + + private final String styleSheet; + + public ResourcesComponentLibrary(String directory) { + Objects.requireNonNull(directory); + LOGGER.info("Loading component library from {}...", directory); + + components = Components.load(directory).getComponents() + .stream() + .collect(Collectors.toMap(c -> c.getMetadata().getType(), c -> c)); + + // preload SVG documents + SVGLoaderToDocument svgLoadDoc = new SVGLoaderToDocument(); + for (Component component : components.values()) { + String resourceName = directory + "/" + component.getFileName(); + LOGGER.debug("Reading component {}", resourceName); + SVGOMDocument doc = svgLoadDoc.read(resourceName); + svgDocuments.put(component.getMetadata().getType(), doc); + } + try { + styleSheet = new String(ByteStreams.toByteArray(getClass().getResourceAsStream(directory + "/" + "components.css")), StandardCharsets.UTF_8); + } catch (IOException e) { + throw new UncheckedIOException("Can't read css file from the SVG library!", e); + } + } + + @Override + public SVGOMDocument getSvgDocument(String type) { + Objects.requireNonNull(type); + return svgDocuments.get(type); + } + + @Override + public List getAnchorPoints(String type) { + Objects.requireNonNull(type); + Component component = components.get(type); + return component != null ? component.getMetadata().getAnchorPoints() + : Collections.singletonList(new AnchorPoint(0, 0, AnchorOrientation.NONE)); + } + + @Override + public ComponentSize getSize(String type) { + Objects.requireNonNull(type); + Component component = components.get(type); + return component != null ? component.getMetadata().getSize() : new ComponentSize(0, 0); + } + + public String getStyleSheet() { + return styleSheet; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractBlock.java new file mode 100644 index 000000000..65c2ac4da --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractBlock.java @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.powsybl.sld.layout.LayoutParameters; + +import java.io.IOException; +import java.util.EnumMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public abstract class AbstractBlock implements Block { + + protected final Type type; + + private Map cardinality; + + private Block parentBlock; + + private Cell cell; + + private Position position; + + private Coord coord; + + /** + * Constructor for primary layout.block with the list of nodes corresponding to the + * layout.block + */ + AbstractBlock(Type type) { + cardinality = new EnumMap<>(Extremity.class); + cardinality.put(Extremity.START, 0); + cardinality.put(Extremity.END, 0); + this.type = Objects.requireNonNull(type); + position = new Position(-1, -1); + coord = new Coord(-1, -1); + } + + @Override + public Node getStartingNode() { + return getExtremityNode(Extremity.START); + } + + @Override + public Node getEndingNode() { + return getExtremityNode(Extremity.END); + } + + @Override + public Extremity getExtremity(Node node) { + if (node.equals(getExtremityNode(Extremity.START))) { + return Extremity.START; + } + if (node.equals(getExtremityNode(Extremity.END))) { + return Extremity.END; + } + return Extremity.NONE; + } + + @Override + public int getCardinality(Node node) { + return getCardinality(getExtremity(node)); + } + + @Override + public int getCardinality(Extremity extremity) { + return cardinality.get(extremity); + } + + @Override + public void setCardinality(Extremity extremity, int i) { + cardinality.put(extremity, i); + } + + public Block getParentBlock() { + return parentBlock; + } + + @Override + public void setParentBlock(Block parentBlock) { + this.parentBlock = parentBlock; + } + + @Override + public Cell getCell() { + return cell; + } + + @Override + public void setCell(Cell cell) { + this.cell = cell; + if (cell == null) { + return; + } + if (cell.getType() == Cell.CellType.SHUNT) { + setOrientation(Orientation.HORIZONTAL); + } else { + setOrientation(Orientation.VERTICAL); + } + } + + @Override + public Position getPosition() { + return position; + } + + @Override + public void setOrientation(Orientation orientation) { + getPosition().setOrientation(orientation); + } + + @Override + public Coord getCoord() { + return coord; + } + + @Override + public void setXSpan(double xSpan) { + getCoord().setXSpan(xSpan); + } + + @Override + public void setYSpan(double ySpan) { + getCoord().setYSpan(ySpan); + } + + @Override + public void setX(double x) { + getCoord().setX(x); + } + + @Override + public void setY(double y) { + getCoord().setY(y); + } + + @Override + public void calculateCoord(LayoutParameters layoutParam) { + if (getPosition().getOrientation() == Orientation.VERTICAL) { + coordVerticalCase(layoutParam); + } else { + coordHorizontalCase(layoutParam); + } + } + + @Override + public void calculateRootCoord(LayoutParameters layoutParam) { + double dyToBus = 0; + coord.setXSpan((double) position.getHSpan() * layoutParam.getCellWidth()); + if (cell.getType() == Cell.CellType.INTERN) { + coord.setYSpan(0); + if (((InternCell) cell).getDirection() != BusCell.Direction.FLAT) { + dyToBus = layoutParam.getInternCellHeight() * position.getV(); + } + } else { + coord.setYSpan(layoutParam.getExternCellHeight()); + dyToBus = layoutParam.getExternCellHeight() / 2 + layoutParam.getStackHeight(); + } + + coord.setX(layoutParam.getInitialXBus() + + layoutParam.getCellWidth() * position.getH() + + coord.getXSpan() / 2); + + switch (((BusCell) cell).getDirection()) { + case BOTTOM: + coord.setY(layoutParam.getInitialYBus() + + (((BusCell) cell).getMaxBusPosition().getV() - 1) * layoutParam.getVerticalSpaceBus() + + dyToBus); + break; + case TOP: + coord.setY(layoutParam.getInitialYBus() + - dyToBus); + break; + case FLAT: + coord.setY(layoutParam.getInitialYBus() + + (getPosition().getV() - 1) * layoutParam.getVerticalSpaceBus()); + break; + default: + } + calculateCoord(layoutParam); + } + + @Override + public Block.Type getType() { + return this.type; + } + + protected abstract void writeJsonContent(JsonGenerator generator) throws IOException; + + @Override + public void writeJson(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + generator.writeStringField("type", type.name()); + writeJsonContent(generator); + generator.writeEndObject(); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractBusCell.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractBusCell.java new file mode 100644 index 000000000..2d4110767 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractBusCell.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.sld.layout.LayoutParameters; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public abstract class AbstractBusCell extends AbstractCell implements BusCell { + + private List primaryLegBlocks = new ArrayList<>(); + private Direction direction = Direction.UNDEFINED; + + protected AbstractBusCell(Graph graph, CellType type) { + super(graph, type); + } + + @Override + public void blocksSetting(Block rootBlock, List primaryBlocksConnectedToBus) { + setRootBlock(rootBlock); + this.primaryLegBlocks = new ArrayList<>(primaryBlocksConnectedToBus); + } + + @Override + public List getBusNodes() { + return nodes.stream() + .filter(n -> n.getType() == Node.NodeType.BUS) + .map(BusNode.class::cast) + .collect(Collectors.toList()); + } + + @Override + public List getPrimaryLegBlocks() { + return new ArrayList<>(primaryLegBlocks); + } + + @Override + public Direction getDirection() { + return direction; + } + + @Override + public void setDirection(Direction direction) { + this.direction = direction; + } + + @Override + public Position getMaxBusPosition() { + return graph.getMaxBusStructuralPosition(); + } + + @Override + public void calculateCoord(LayoutParameters layoutParam) { + getRootBlock().calculateRootCoord(layoutParam); + } + + @Override + public String toString() { + return "Cell(type=" + getType() + ", direction=" + direction + ", nodes=" + nodes + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractCell.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractCell.java new file mode 100644 index 000000000..1c46e13b8 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractCell.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.fasterxml.jackson.core.JsonGenerator; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ + +public abstract class AbstractCell implements Cell { + final Graph graph; + private CellType type; + private int number; + protected final List nodes = new ArrayList<>(); + + private Block rootBlock; + + protected AbstractCell(Graph graph, CellType type) { + this.graph = Objects.requireNonNull(graph); + this.type = Objects.requireNonNull(type); + number = graph.getNextCellIndex(); + graph.addCell(this); + } + + public void addNodes(Collection nodesToAdd) { + nodes.addAll(nodesToAdd); + } + + public List getNodes() { + return new ArrayList<>(nodes); + } + + public void removeAllNodes(List nodeToRemove) { + nodes.removeAll(nodeToRemove); + } + + public void setNodes(List nodes) { + this.nodes.addAll(nodes); + // the cell of the node of a SHUNT node (which belongs to a SHUNT and an EXTERN cells) + // is the cell of the EXTERN cell + if (type != CellType.SHUNT) { + nodes.forEach(node -> node.setCell(this)); + } else { + nodes.stream().filter(node -> node.getType() != Node.NodeType.SHUNT).forEach(node -> node.setCell(this)); + } + } + + public void setType(CellType type) { + this.type = type; + } + + public CellType getType() { + return this.type; + } + + public Block getRootBlock() { + return rootBlock; + } + + public void setRootBlock(Block rootBlock) { + this.rootBlock = rootBlock; + } + + public int getNumber() { + return number; + } + + public void writeJson(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + generator.writeStringField("type", type.name()); + if (rootBlock != null) { + generator.writeFieldName("rootBlock"); + rootBlock.writeJson(generator); + } + generator.writeEndObject(); + } + + public String getFullId() { + return type + nodes.stream().map(Node::getId).sorted().collect(Collectors.toList()).toString(); + } + + @Override + public String toString() { + return "Cell(type=" + type + ", nodes=" + nodes + ")"; + } + + public Graph getGraph() { + return graph; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractComposedBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractComposedBlock.java new file mode 100644 index 000000000..b4ae8db0b --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractComposedBlock.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.fasterxml.jackson.core.JsonGenerator; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * @author Benoit Jeanson + */ +public abstract class AbstractComposedBlock extends AbstractBlock implements ComposedBlock { + + List subBlocks; + + AbstractComposedBlock(Type type, List subBlocks) { + super(type); + if (subBlocks.isEmpty()) { + throw new IllegalArgumentException("Empty block list"); + } + subBlocks.forEach(b -> b.setParentBlock(this)); + } + + @Override + public Graph getGraph() { + return subBlocks.get(0).getGraph(); + } + + public List getSubBlocks() { + return subBlocks; + } + + @Override + public boolean isEmbedingNodeType(Node.NodeType type) { + return subBlocks.stream().anyMatch(b -> b.isEmbedingNodeType(type)); + } + + @Override + public int getOrder() { + return getExtremityNode(Block.Extremity.END).getType() == Node.NodeType.FEEDER ? + ((FeederNode) getExtremityNode(Block.Extremity.END)).getOrder() : 0; + } + + @Override + public Node getExtremityNode(Extremity extremity) { + if (extremity == Extremity.START) { + return subBlocks.get(0).getExtremityNode(Extremity.START); + } + if (extremity == Extremity.END) { + return subBlocks.get(subBlocks.size() - 1).getExtremityNode(Extremity.END); + } + return null; + } + + @Override + public void reverseBlock() { + Collections.reverse(subBlocks); + subBlocks.forEach(Block::reverseBlock); + } + + @Override + public void setOrientation(Orientation orientation) { + super.setOrientation(orientation); + subBlocks.forEach(sub -> sub.setOrientation(orientation)); + } + + @Override + protected void writeJsonContent(JsonGenerator generator) throws IOException { + generator.writeFieldName("nodes"); + generator.writeStartArray(); + for (Block subBlock : subBlocks) { + subBlock.writeJson(generator); + } + generator.writeEndArray(); + } + + @Override + public String toString() { + return "BodyParallelBlock(subBlocks=" + subBlocks + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractParallelBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractParallelBlock.java new file mode 100644 index 000000000..bdf8dd6e1 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractParallelBlock.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.sld.layout.LayoutParameters; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +abstract class AbstractParallelBlock extends AbstractComposedBlock implements ParallelBlock { + + AbstractParallelBlock(List subBlocks, Cell cell, boolean allowMerge) { + super(Type.PARALLEL, subBlocks); + this.subBlocks = new ArrayList<>(); + subBlocks.forEach(child -> { + if (child.getType() == Type.PARALLEL && allowMerge) { + this.subBlocks.addAll(((ParallelBlock) child).getSubBlocks()); + } else { + this.subBlocks.add(child); + } + }); + setCell(cell); + + Node node0s = subBlocks.get(0).getExtremityNode(Block.Extremity.START); + Node node0e = subBlocks.get(0).getExtremityNode(Block.Extremity.END); + subBlocks.forEach(b -> { + b.setParentBlock(this); + if (b.getExtremityNode(Block.Extremity.START) != node0s && b.getExtremityNode(Block.Extremity.END) != node0e) { + b.reverseBlock(); + } + }); + + setCardinality(Extremity.START, this.subBlocks.size()); + setCardinality(Extremity.END, this.subBlocks.size()); + } + + abstract double initX0(); + + abstract double intitXStep(); + + @Override + public void coordVerticalCase(LayoutParameters layoutParam) { + final double x0Final = initX0(); + final double xPxStepFinal = intitXStep(); + subBlocks.forEach(sub -> { + sub.setX(x0Final + (sub.getPosition().getH() + (double) sub.getPosition().getHSpan() / 2) * xPxStepFinal); + sub.setXSpan(xPxStepFinal * sub.getPosition().getHSpan()); + sub.setY(getCoord().getY()); + sub.setYSpan(getCoord().getYSpan()); + sub.calculateCoord(layoutParam); + }); + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractPrimaryBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractPrimaryBlock.java new file mode 100644 index 000000000..9bca4c175 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/AbstractPrimaryBlock.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.powsybl.commons.PowsyblException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public abstract class AbstractPrimaryBlock extends AbstractBlock implements PrimaryBlock { + + protected final List nodes; + + protected final List stackableBlocks; + + /** + * Constructor. + * A layout.block primary is oriented in order to have : + *
    + *
  • BUS - when in the layout.block - as starting node + *
  • FEEDER - when in the layout.block - as ending node + *
+ * + * @param nodes nodes + */ + + AbstractPrimaryBlock(List nodes, Cell cell) { + super(Type.PRIMARY); + if (nodes.isEmpty()) { + throw new PowsyblException("Empty node list"); + } + this.stackableBlocks = new ArrayList<>(); + this.nodes = new ArrayList<>(nodes); + setCardinality(Extremity.START, 1); + setCardinality(Extremity.END, 1); + setCell(cell); + } + + @Override + public Graph getGraph() { + return nodes.get(0).getGraph(); + } + + @Override + public boolean isEmbedingNodeType(Node.NodeType type) { + return nodes.stream().anyMatch(n -> n.getType() == type); + } + + public List getNodes() { + return new ArrayList<>(nodes); + } + + @Override + public void reverseBlock() { + Collections.reverse(nodes); + } + + @Override + public Node getExtremityNode(Extremity extremity) { + if (extremity == Extremity.START) { + return nodes.get(0); + } + if (extremity == Extremity.END) { + return nodes.get(nodes.size() - 1); + } + return null; + } + + @Override + public int getOrder() { + return getExtremityNode(Extremity.START).getType() == Node.NodeType.FEEDER ? + ((FeederNode) getExtremityNode(Extremity.START)).getOrder() : 0; + } + + // TODO : this should be LegPrimaryBlock + public void addStackableBlock(PrimaryBlock block) { + stackableBlocks.add(block); + } + + public List getStackableBlocks() { + return new ArrayList<>(stackableBlocks); + } + + @Override + protected void writeJsonContent(JsonGenerator generator) throws IOException { + generator.writeFieldName("nodes"); + generator.writeStartArray(); + for (Node node : nodes) { + node.writeJson(generator); + } + generator.writeEndArray(); + } + + @Override + public String toString() { + return "PrimaryBlock(nodes=" + nodes + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ArrowNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ArrowNode.java new file mode 100644 index 000000000..82849af27 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ArrowNode.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import static com.powsybl.sld.library.ComponentTypeName.ARROW; + +/** + * @author Giovanni Ferrari + */ +public class ArrowNode extends Node { + + public ArrowNode(Graph graph) { + super(NodeType.OTHER, "", "", ARROW, false, graph); + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BaseNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BaseNode.java new file mode 100644 index 000000000..ce1845911 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BaseNode.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public interface BaseNode { + + String getId(); + + String getComponentType(); + + boolean isRotated(); + + double getX(); + + double getY(); + + Double getRotationAngle(); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Block.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Block.java new file mode 100644 index 000000000..0baffe487 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Block.java @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.powsybl.sld.layout.LayoutParameters; + +import java.io.IOException; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface Block { + enum Type { + PRIMARY, PARALLEL, SERIAL, UNDEFINED + } + + enum Extremity { + START, END, NONE; + + public Extremity flip() { + if (this.equals(START)) { + return END; + } + if (this.equals(END)) { + return START; + } + return NONE; + } + } + + Graph getGraph(); + + Node getExtremityNode(Extremity extremity); + + Extremity getExtremity(Node node); + + Node getStartingNode(); + + Node getEndingNode(); + + void reverseBlock(); + + boolean isEmbedingNodeType(Node.NodeType type); + + void setParentBlock(Block parentBlock); + + Position getPosition(); + + Coord getCoord(); + + void setXSpan(double xSpan); + + void setYSpan(double ySpan); + + void setX(double x); + + void setY(double y); + + + /** + * Calculate maximal pxWidth that layout.block can use in a cell without modifying + * root pxWidth + */ + void sizing(); + + /** + * Calculates all the blocks dimensions and find the order of the layout.block inside + * the cell + */ + void calculateCoord(LayoutParameters layoutParam); + + void calculateRootCoord(LayoutParameters layoutParam); + + int getOrder(); + + void coordVerticalCase(LayoutParameters layoutParam); + + void coordHorizontalCase(LayoutParameters layoutParam); + + void setCardinality(Extremity extremity, int i); + + int getCardinality(Extremity extremity); + + int getCardinality(Node commonNode); + + void setCell(Cell cell); + + Cell getCell(); + + void setOrientation(Orientation orientation); + + Type getType(); + + void writeJson(JsonGenerator generator) throws IOException; +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BodyParallelBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BodyParallelBlock.java new file mode 100644 index 000000000..c4f4ae714 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BodyParallelBlock.java @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.sld.layout.LayoutParameters; + +import java.util.Comparator; +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class BodyParallelBlock extends AbstractParallelBlock { + + public BodyParallelBlock(List subBlocks, Cell cell, boolean allowMerge) { + super(subBlocks, cell, allowMerge); + } + + @Override + public void sizing() { + subBlocks.forEach(Block::sizing); + if (getPosition().getOrientation() == Orientation.VERTICAL) { + getPosition().setVSpan(subBlocks.stream().mapToInt(b -> b.getPosition().getVSpan()).max().orElse(0)); + subBlocks.sort(Comparator.comparingInt(Block::getOrder)); + getPosition().setHSpan(subBlocks.stream().mapToInt(b -> b.getPosition().getHSpan()).sum()); + int h = 0; + for (Block block : subBlocks) { + block.getPosition().setHV(h, 0); + h += block.getPosition().getHSpan(); + } + } else { + getPosition().setVSpan(subBlocks.stream().mapToInt(b -> b.getPosition().getVSpan()).sum()); + getPosition().setHSpan(subBlocks.stream().mapToInt(b -> b.getPosition().getHSpan()).max().orElse(0)); + int v = 0; + for (Block subBlock : subBlocks) { + subBlock.getPosition().setHV(0, v); + v += subBlock.getPosition().getVSpan(); + } + } + } + + @Override + double initX0() { + return getCoord().getX() - getCoord().getXSpan() / 2; + } + + @Override + double intitXStep() { + return getCoord().getXSpan() / getPosition().getHSpan(); + } + + @Override + public void coordHorizontalCase(LayoutParameters layoutParam) { + subBlocks.forEach(sub -> { + sub.setX(getCoord().getX()); + sub.setXSpan(getCoord().getXSpan()); + sub.setY(getCoord().getY()); + sub.setYSpan(getCoord().getYSpan()); + sub.calculateCoord(layoutParam); + }); + } + + @Override + public String toString() { + return "BodyParallelBlock(subBlocks=" + subBlocks + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BodyPrimaryBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BodyPrimaryBlock.java new file mode 100644 index 000000000..288f82c74 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BodyPrimaryBlock.java @@ -0,0 +1,105 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.sld.layout.LayoutParameters; + +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class BodyPrimaryBlock extends AbstractPrimaryBlock { + + public BodyPrimaryBlock(List nodes, Cell cell) { + super(nodes, cell); + if (getExtremityNode(Extremity.START).getType() == Node.NodeType.FEEDER) { + reverseBlock(); + } + } + + public BodyPrimaryBlock(BodyPrimaryBlock bodyPrimaryBlock) { + this(bodyPrimaryBlock.getNodes(), bodyPrimaryBlock.getCell()); + } + + // TODO : START or END ? + @Override + public int getOrder() { + return getExtremityNode(Block.Extremity.START).getType() == Node.NodeType.FEEDER ? + ((FeederNode) getExtremityNode(Block.Extremity.START)).getOrder() : 0; + } + + @Override + public void sizing() { + if (getPosition().getOrientation() == Orientation.VERTICAL) { + getPosition().setHSpan(1); + // in the case of vertical Blocks the x Spanning is a ratio of the nb of edges of the blocks/overall edges + getPosition().setVSpan(nodes.size() - 1); + } else { + // in the case of horizontal Blocks having 1 switch/1 position => 1 hPos / 2 edges rounded to the superior int + getPosition().setHSpan(nodes.size() - 2); + getPosition().setVSpan(1); + } + } + + @Override + public void coordVerticalCase(LayoutParameters layoutParam) { + int sign = ((BusCell) getCell()).getDirection() == BusCell.Direction.TOP ? 1 : -1; + double y0 = getCoord().getY() + sign * getCoord().getYSpan() / 2; + double yPxStep = calcYPxStep(sign); + int v = 0; + for (Node node : nodes) { + node.setX(getCoord().getX()); + node.setY(y0 - yPxStep * v); + node.setRotationAngle(null); + v++; + } + } + + @Override + public void coordHorizontalCase(LayoutParameters layoutParam) { + double x0 = getCoord().getX() - getCoord().getXSpan() / 2; + if (getCell().getType() == Cell.CellType.INTERN + && ((BusCell) getCell()).getDirection() != BusCell.Direction.FLAT) { + x0 += layoutParam.getCellWidth() / 2; + } + double xPxStep = getCoord().getXSpan() / (nodes.size() - 1); + int h = 0; + for (Node node : nodes) { + node.setY(getCoord().getY()); + node.setX(x0 + xPxStep * h); + node.setRotationAngle(90.); + h++; + } + } + + void coordShuntCase() { + double x0 = getExtremityNode(Block.Extremity.START).getX(); + double x1 = getExtremityNode(Block.Extremity.END).getX(); + double y0 = getExtremityNode(Block.Extremity.START).getY(); + double y1 = getExtremityNode(Block.Extremity.END).getY(); + double dx = (x1 - x0) / (nodes.size() - 1); + double dy = (y1 - y0) / (nodes.size() - 1); + for (int i = 1; i < nodes.size() - 1; i++) { + Node node = nodes.get(i); + node.setX(x0 + i * dx, false, false); + node.setY(y0 + i * dy, false, false); + if (dy == 0) { + node.setRotationAngle(90.); + } + } + } + + private double calcYPxStep(int sign) { + if (getPosition().getVSpan() == 0) { + return 0; + } + return sign * getCoord().getYSpan() / (nodes.size() - 1); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BusCell.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BusCell.java new file mode 100644 index 000000000..a12cdcce2 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BusCell.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.*; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface BusCell extends Cell { + + enum Direction { + TOP, BOTTOM, FLAT, UNDEFINED + } + + List getBusNodes(); + + void blocksSetting(Block rootBlock, List primaryBlocksConnectedToBus); + + List getPrimaryLegBlocks(); + + void blockSizing(); + + int newHPosition(int hPosition); + + Direction getDirection(); + + void setDirection(Direction direction); + + Position getMaxBusPosition(); + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BusNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BusNode.java new file mode 100644 index 000000000..4843aab8b --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/BusNode.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.sld.layout.LayoutParameters; + +import java.util.Objects; + +import static com.powsybl.sld.library.ComponentTypeName.BUSBAR_SECTION; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class BusNode extends Node { + + private double pxWidth = 1; + + private Position structuralPosition; + + private Position position = new Position(-1, -1); + + protected BusNode(String id, String name, boolean fictitious, Graph graph) { + super(NodeType.BUS, id, name, BUSBAR_SECTION, fictitious, graph); + } + + public static BusNode create(Graph graph, BusbarSection busbarSection) { + Objects.requireNonNull(graph); + Objects.requireNonNull(busbarSection); + return new BusNode(busbarSection.getId(), busbarSection.getName(), false, graph); + } + + public static BusNode create(Graph graph, Bus bus) { + Objects.requireNonNull(graph); + Objects.requireNonNull(bus); + return new BusNode(bus.getId(), bus.getName(), false, graph); + } + + public static BusNode createFictitious(Graph graph, String id) { + return new BusNode(id, id, true, graph); + } + + public void calculateCoord(LayoutParameters layoutParameters) { + setY(layoutParameters.getInitialYBus() + + (position.getV() - 1) * layoutParameters.getVerticalSpaceBus()); + setX(layoutParameters.getInitialXBus() + + position.getH() * layoutParameters.getCellWidth() + + layoutParameters.getHorizontalBusPadding() / 2); + setPxWidth(position.getHSpan() * layoutParameters.getCellWidth() - layoutParameters.getHorizontalBusPadding()); + } + + @Override + public void setCell(Cell cell) { + if (!(cell instanceof BusCell)) { + throw new PowsyblException("The Cell of a BusNode shall be a BusCell"); + } + super.setCell(cell); + } + + public double getPxWidth() { + return pxWidth; + } + + public void setPxWidth(double widthBus) { + this.pxWidth = widthBus; + } + + public Position getPosition() { + return position; + } + + public void setPosition(Position position) { + this.position = position; + } + + public Position getStructuralPosition() { + return structuralPosition; + } + + public void setStructuralPosition(Position structuralPosition) { + this.structuralPosition = structuralPosition; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Cell.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Cell.java new file mode 100644 index 000000000..cae6656ff --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Cell.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.powsybl.sld.layout.LayoutParameters; + +import java.io.IOException; +import java.util.*; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface Cell { + enum CellType { + INTERN, EXTERN, SHUNT + } + + void addNodes(Collection nodesToAdd); + + List getNodes(); + + void removeAllNodes(List nodeToRemove); + + void setNodes(List nodes); + + void setType(CellType type); + + CellType getType(); + + Block getRootBlock(); + + void setRootBlock(Block rootBlock); + + int getNumber(); + + void calculateCoord(LayoutParameters layoutParam); + + void writeJson(JsonGenerator generator) throws IOException; + + String getFullId(); + + Graph getGraph(); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ComposedBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ComposedBlock.java new file mode 100644 index 000000000..2033764e0 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ComposedBlock.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface ComposedBlock extends Block { + + List getSubBlocks(); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Coord.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Coord.java new file mode 100644 index 000000000..05939c56d --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Coord.java @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.Objects; + +/** + * class use to store relatives coordinates of a nodeBus + * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class Coord { + private double x; + private double y; + + private double xSpan; + private double ySpan; + + public Coord(double x, double y) { + this.x = x; + this.y = y; + } + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } + + public double getXSpan() { + return xSpan; + } + + public void setXSpan(double xSpan) { + this.xSpan = xSpan; + } + + public double getYSpan() { + return ySpan; + } + + public void setYSpan(double ySpan) { + this.ySpan = ySpan; + } + + @Override + public int hashCode() { + return Objects.hash(x, y, xSpan, ySpan); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Coord) { + Coord other = (Coord) obj; + return other.x == x + && other.y == y + && other.xSpan == xSpan + && other.ySpan == ySpan; + } + return false; + } + + @Override + public String toString() { + return "Coord(x=" + x + ", y=" + y + ", xSpan=" + xSpan + ", ySpan=" + ySpan + ")"; + } +} + diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Edge.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Edge.java new file mode 100644 index 000000000..8b4a55ac0 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Edge.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class Edge { + + private final Node node1; + + private final Node node2; + + /** + * Constructor + * @param node1 node1 + * @param node2 node2 + */ + public Edge(Node node1, Node node2) { + this.node1 = Objects.requireNonNull(node1); + this.node2 = Objects.requireNonNull(node2); + } + + public Node getNode1() { + return node1; + } + + public Node getNode2() { + return node2; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ExternCell.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ExternCell.java new file mode 100644 index 000000000..946e67ee5 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ExternCell.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class ExternCell extends AbstractBusCell { + private int order = -1; + + public ExternCell(Graph graph) { + super(graph, CellType.EXTERN); + } + + public void orderFromFeederOrders() { + int sumOrder = 0; + int nbFeeder = 0; + for (FeederNode node : getNodes().stream() + .filter(node -> node.getType() == Node.NodeType.FEEDER) + .map(node -> (FeederNode) node).collect(Collectors.toList())) { + sumOrder += node.getOrder(); + nbFeeder++; + } + if (nbFeeder != 0) { + setOrder(sumOrder / nbFeeder); + } + } + + @Override + public void blockSizing() { + getRootBlock().sizing(); + } + + @Override + public int newHPosition(int hPosition) { + getRootBlock().getPosition().setHV(hPosition, 0); + return hPosition + getRootBlock().getPosition().getHSpan(); + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + @Override + public String toString() { + return "ExternCell(order=" + order + ", direction=" + getDirection() + ", nodes=" + nodes + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Feeder2WTNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Feeder2WTNode.java new file mode 100644 index 000000000..29d3e1a65 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Feeder2WTNode.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.iidm.network.VoltageLevel; + +import java.util.Objects; + +import static com.powsybl.sld.library.ComponentTypeName.PHASE_SHIFT_TRANSFORMER; +import static com.powsybl.sld.library.ComponentTypeName.TWO_WINDINGS_TRANSFORMER; + +/** + * @author Franck Lecuyer + */ +public class Feeder2WTNode extends FeederBranchNode { + protected Feeder2WTNode(String id, String name, String componentType, boolean fictitious, Graph graph, VoltageLevel vlOtherSide) { + super(id, name, componentType, fictitious, graph, vlOtherSide); + } + + public static FeederNode create(Graph graph, TwoWindingsTransformer branch, TwoWindingsTransformer.Side side) { + Objects.requireNonNull(graph); + Objects.requireNonNull(branch); + String componentType; + + if (branch.getPhaseTapChanger() == null) { + componentType = TWO_WINDINGS_TRANSFORMER; + } else { + componentType = PHASE_SHIFT_TRANSFORMER; + } + + String id = branch.getId() + "_" + side.name(); + String name = branch.getName() + "_" + side.name(); + TwoWindingsTransformer.Side otherSide = side == TwoWindingsTransformer.Side.ONE + ? TwoWindingsTransformer.Side.TWO + : TwoWindingsTransformer.Side.ONE; + VoltageLevel vlOtherSide = branch.getTerminal(otherSide).getVoltageLevel(); + return new Feeder2WTNode(id, name, componentType, false, graph, vlOtherSide); + } + + public static Feeder2WTNode create(Graph graph, String id, String name, VoltageLevel vlOtherSide) { + Objects.requireNonNull(graph); + Objects.requireNonNull(vlOtherSide); + return new Feeder2WTNode(id, name, TWO_WINDINGS_TRANSFORMER, false, graph, vlOtherSide); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Feeder3WTNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Feeder3WTNode.java new file mode 100644 index 000000000..abcb2ba06 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Feeder3WTNode.java @@ -0,0 +1,94 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.VoltageLevel; + +import java.util.Objects; + +import static com.powsybl.sld.library.ComponentTypeName.THREE_WINDINGS_TRANSFORMER; + +/** + * @author Franck Lecuyer + */ +public class Feeder3WTNode extends FeederNode { + + private ThreeWindingsTransformer transformer; + private ThreeWindingsTransformer.Side side; + + protected Feeder3WTNode(String id, String name, String componentType, + boolean fictitious, Graph graph, + ThreeWindingsTransformer transformer, + ThreeWindingsTransformer.Side side) { + super(id, name, componentType, fictitious, graph); + this.transformer = transformer; + this.side = side; + } + + public static Feeder3WTNode create(Graph graph, ThreeWindingsTransformer twt, ThreeWindingsTransformer.Side side) { + Objects.requireNonNull(graph); + Objects.requireNonNull(twt); + Objects.requireNonNull(side); + String id = twt.getId() + "_" + side.name(); + String name = twt.getName() + "_" + side.name(); + return new Feeder3WTNode(id, name, THREE_WINDINGS_TRANSFORMER, + false, graph, twt, side); + } + + public String getId2() { + String ret = null; + switch (side) { + case ONE: ret = ThreeWindingsTransformer.Side.TWO.name(); break; + case TWO: ret = ThreeWindingsTransformer.Side.ONE.name(); break; + case THREE: ret = ThreeWindingsTransformer.Side.ONE.name(); break; + } + return ret; + } + + public String getName2() { + return getId2(); + } + + public VoltageLevel getVL2() { + VoltageLevel ret = null; + switch (side) { + case ONE: ret = transformer.getTerminal(ThreeWindingsTransformer.Side.TWO).getVoltageLevel(); break; + case TWO: ret = transformer.getTerminal(ThreeWindingsTransformer.Side.ONE).getVoltageLevel(); break; + case THREE: ret = transformer.getTerminal(ThreeWindingsTransformer.Side.ONE).getVoltageLevel(); break; + } + return ret; + } + + public String getId3() { + String ret = null; + switch (side) { + case ONE: ret = ThreeWindingsTransformer.Side.THREE.name(); break; + case TWO: ret = ThreeWindingsTransformer.Side.THREE.name(); break; + case THREE: ret = ThreeWindingsTransformer.Side.TWO.name(); break; + } + return ret; + } + + public String getName3() { + return getId3(); + } + + public VoltageLevel getVL3() { + VoltageLevel ret = null; + switch (side) { + case ONE: ret = transformer.getTerminal(ThreeWindingsTransformer.Side.THREE).getVoltageLevel(); break; + case TWO: ret = transformer.getTerminal(ThreeWindingsTransformer.Side.THREE).getVoltageLevel(); break; + case THREE: ret = transformer.getTerminal(ThreeWindingsTransformer.Side.TWO).getVoltageLevel(); break; + } + return ret; + } + + public ThreeWindingsTransformer getTransformer() { + return transformer; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederBranchNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederBranchNode.java new file mode 100644 index 000000000..46a7babeb --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederBranchNode.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.iidm.network.VoltageLevel; + +/** + * @author Franck Lecuyer + */ +public class FeederBranchNode extends FeederNode { + + private VoltageLevel vlOtherSide; + + protected FeederBranchNode(String id, String name, String componentType, boolean fictitious, Graph graph, VoltageLevel vlOtherSide) { + super(id, name, componentType, fictitious, graph); + this.vlOtherSide = vlOtherSide; + } + + public VoltageLevel getVlOtherSide() { + return vlOtherSide; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederLineNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederLineNode.java new file mode 100644 index 000000000..14c2816c2 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederLineNode.java @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.iidm.network.Branch; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.VoltageLevel; + +import java.util.Objects; + +import static com.powsybl.sld.library.ComponentTypeName.LINE; + +/** + * @author Franck Lecuyer + */ +public class FeederLineNode extends FeederBranchNode { + protected FeederLineNode(String id, String name, String componentType, boolean fictitious, Graph graph, VoltageLevel vlOtherSide) { + super(id, name, componentType, fictitious, graph, vlOtherSide); + } + + public static FeederNode create(Graph graph, Line line, Branch.Side side) { + Objects.requireNonNull(graph); + Objects.requireNonNull(line); + + String id = line.getId() + "_" + side.name(); + String name = line.getName() + "_" + side.name(); + Branch.Side otherSide = side == Branch.Side.ONE ? Branch.Side.TWO : Branch.Side.ONE; + VoltageLevel vlOtherSide = line.getTerminal(otherSide).getVoltageLevel(); + return new FeederLineNode(id, name, LINE, false, graph, vlOtherSide); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederNode.java new file mode 100644 index 000000000..7d5c933d9 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FeederNode.java @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.Branch; +import com.powsybl.iidm.network.Injection; +import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.iidm.network.TwoWindingsTransformer; + +import java.util.Objects; + +import static com.powsybl.sld.library.ComponentTypeName.CAPACITOR; +import static com.powsybl.sld.library.ComponentTypeName.DANGLING_LINE; +import static com.powsybl.sld.library.ComponentTypeName.GENERATOR; +import static com.powsybl.sld.library.ComponentTypeName.INDUCTOR; +import static com.powsybl.sld.library.ComponentTypeName.LINE; +import static com.powsybl.sld.library.ComponentTypeName.LOAD; +import static com.powsybl.sld.library.ComponentTypeName.NODE; +import static com.powsybl.sld.library.ComponentTypeName.PHASE_SHIFT_TRANSFORMER; +import static com.powsybl.sld.library.ComponentTypeName.STATIC_VAR_COMPENSATOR; +import static com.powsybl.sld.library.ComponentTypeName.TWO_WINDINGS_TRANSFORMER; +import static com.powsybl.sld.library.ComponentTypeName.VSC_CONVERTER_STATION; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class FeederNode extends Node { + + private int order = -1; + + private BusCell.Direction direction = BusCell.Direction.UNDEFINED; + + protected FeederNode(String id, String name, String componentType, boolean fictitious, Graph graph) { + super(NodeType.FEEDER, id, name, componentType, fictitious, graph); + } + + public static FeederNode create(Graph graph, Injection injection) { + Objects.requireNonNull(graph); + Objects.requireNonNull(injection); + String componentType; + switch (injection.getType()) { + case GENERATOR: + componentType = GENERATOR; + break; + case LOAD: + componentType = LOAD; + break; + case HVDC_CONVERTER_STATION: + componentType = VSC_CONVERTER_STATION; + break; + case STATIC_VAR_COMPENSATOR: + componentType = STATIC_VAR_COMPENSATOR; + break; + case SHUNT_COMPENSATOR: + componentType = ((ShuntCompensator) injection).getbPerSection() >= 0 ? CAPACITOR : INDUCTOR; + break; + case DANGLING_LINE: + componentType = DANGLING_LINE; + break; + default: + throw new AssertionError(); + } + return new FeederNode(injection.getId(), injection.getName(), componentType, false, graph); + } + + public static FeederNode create(Graph graph, Branch branch, Branch.Side side) { + Objects.requireNonNull(graph); + Objects.requireNonNull(branch); + String componentType; + switch (branch.getType()) { + case LINE: + componentType = LINE; + break; + case TWO_WINDINGS_TRANSFORMER: + if (((TwoWindingsTransformer) branch).getPhaseTapChanger() == null) { + componentType = TWO_WINDINGS_TRANSFORMER; + } else { + componentType = PHASE_SHIFT_TRANSFORMER; + } + break; + default: + throw new AssertionError(); + } + String id = branch.getId() + "_" + side.name(); + String name = branch.getName() + "_" + side.name(); + return new FeederNode(id, name, componentType, false, graph); + } + + public static FeederNode createFictitious(Graph graph, String id) { + return new FeederNode(id, id, NODE, true, graph); + } + + public static FeederNode create(Graph graph, String id, String name, String componentType) { + return new FeederNode(id, name, componentType, false, graph); + } + + @Override + public void setCell(Cell cell) { + if (!(cell instanceof ExternCell)) { + throw new PowsyblException("The Cell of a feeder node shall be an ExternCell"); + } + super.setCell(cell); + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public BusCell.Direction getDirection() { + return direction; + } + + public void setDirection(BusCell.Direction direction) { + this.direction = direction; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Fictitious3WTNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Fictitious3WTNode.java new file mode 100644 index 000000000..a9c4febc0 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Fictitious3WTNode.java @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.iidm.network.ThreeWindingsTransformer; + +import static com.powsybl.sld.library.ComponentTypeName.THREE_WINDINGS_TRANSFORMER; + +/** + * @author Franck Lecuyer + */ +public class Fictitious3WTNode extends FictitiousNode { + + private final ThreeWindingsTransformer transformer; + + public Fictitious3WTNode(Graph graph, String id, ThreeWindingsTransformer transformer) { + super(graph, id, THREE_WINDINGS_TRANSFORMER); + this.transformer = transformer; + } + + public ThreeWindingsTransformer getTransformer() { + return transformer; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FictitiousNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FictitiousNode.java new file mode 100644 index 000000000..510073378 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/FictitiousNode.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import static com.powsybl.sld.library.ComponentTypeName.NODE; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class FictitiousNode extends Node { + + public FictitiousNode(Graph graph, String id) { + super(NodeType.FICTITIOUS, id, id, NODE, true, graph); + } + + public FictitiousNode(Graph graph, String id, String componentType) { + super(NodeType.FICTITIOUS, id, id, componentType, true, graph); + } + + public int getCardinality() { + return this.getAdjacentNodes().size() - (getType() == NodeType.SHUNT ? 1 : 0); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Graph.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Graph.java new file mode 100644 index 000000000..2532939ce --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Graph.java @@ -0,0 +1,957 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.powsybl.commons.PowsyblException; +import com.powsybl.commons.util.ServiceLoaderCache; +import com.powsybl.iidm.network.Branch; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.BusbarSection; +import com.powsybl.iidm.network.Connectable; +import com.powsybl.iidm.network.DanglingLine; +import com.powsybl.iidm.network.DefaultTopologyVisitor; +import com.powsybl.iidm.network.Generator; +import com.powsybl.iidm.network.HvdcConverterStation; +import com.powsybl.iidm.network.Injection; +import com.powsybl.iidm.network.Line; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.ShuntCompensator; +import com.powsybl.iidm.network.StaticVarCompensator; +import com.powsybl.iidm.network.Switch; +import com.powsybl.iidm.network.SwitchKind; +import com.powsybl.iidm.network.Terminal; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.postprocessor.GraphBuildPostProcessor; +import org.jgrapht.UndirectedGraph; +import org.jgrapht.alg.ConnectivityInspector; +import org.jgrapht.graph.Pseudograph; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.powsybl.sld.library.ComponentTypeName.INDUCTOR; +import static com.powsybl.sld.library.ComponentTypeName.LINE; + +/** + * This class builds the connectivity among the elements of a voltageLevel + * buildGraphAndDetectCell establishes the List of nodes, edges and nodeBuses + * cells is built by the PatternCellDetector Class + * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class Graph { + + private static final Logger LOGGER = LoggerFactory.getLogger(Graph.class); + + private static final ServiceLoaderCache POST_PROCESSOR_LOADER = new ServiceLoaderCache<>(GraphBuildPostProcessor.class); + + private VoltageLevel voltageLevel; + + private final boolean useName; + + private final List nodes = new ArrayList<>(); + + private final List edges = new ArrayList<>(); + + private final SortedSet cells = new TreeSet<>( + Comparator.comparingInt(Cell::getNumber)); // cells sorted to avoid randomness + + private final Map> nodesByType = new EnumMap<>(Node.NodeType.class); + + private final Map nodesById = new HashMap<>(); + + private Position maxBusStructuralPosition = new Position(0, 0); + + private Map> vPosToHPosToNodeBus; + + private int cellCounter = 0; + + private double x = 0; + private double y = 0; + + private final boolean forVoltageLevelDiagram; // true if voltageLevel diagram + // false if substation diagram + + private final boolean showInductorFor3WT; + + /** + * Constructor + */ + public Graph(VoltageLevel voltageLevel, boolean useName, boolean forVoltageLevelDiagram, boolean showInductorFor3WT) { + this.voltageLevel = Objects.requireNonNull(voltageLevel); + this.useName = useName; + this.forVoltageLevelDiagram = forVoltageLevelDiagram; + this.showInductorFor3WT = showInductorFor3WT; + } + + public boolean isUseName() { + return useName; + } + + public static Graph create(VoltageLevel vl) { + return create(vl, false, true, false); + } + + public static Graph create(VoltageLevel vl, boolean useName, boolean forVoltageLevelDiagram, boolean showInductorFor3WT) { + Objects.requireNonNull(vl); + Graph g = new Graph(vl, useName, forVoltageLevelDiagram, showInductorFor3WT); + g.buildGraph(vl); + return g; + } + + int getNextCellIndex() { + return cellCounter++; + } + + private abstract class AbstractGraphBuilder extends DefaultTopologyVisitor { + + protected abstract void addFeeder(FeederNode node, Terminal terminal); + + @Override + public void visitLoad(Load load) { + addFeeder(FeederNode.create(Graph.this, load), load.getTerminal()); + } + + @Override + public void visitGenerator(Generator generator) { + addFeeder(FeederNode.create(Graph.this, generator), generator.getTerminal()); + } + + @Override + public void visitShuntCompensator(ShuntCompensator sc) { + addFeeder(FeederNode.create(Graph.this, sc), sc.getTerminal()); + } + + @Override + public void visitDanglingLine(DanglingLine danglingLine) { + addFeeder(FeederNode.create(Graph.this, danglingLine), danglingLine.getTerminal()); + } + + @Override + public void visitHvdcConverterStation(HvdcConverterStation converterStation) { + addFeeder(FeederNode.create(Graph.this, converterStation), converterStation.getTerminal()); + } + + @Override + public void visitStaticVarCompensator(StaticVarCompensator staticVarCompensator) { + addFeeder(FeederNode.create(Graph.this, staticVarCompensator), staticVarCompensator.getTerminal()); + } + + @Override + public void visitTwoWindingsTransformer(TwoWindingsTransformer transformer, + TwoWindingsTransformer.Side side) { + addFeeder(Feeder2WTNode.create(Graph.this, transformer, side), transformer.getTerminal(side)); + } + + @Override + public void visitLine(Line line, Line.Side side) { + addFeeder(FeederLineNode.create(Graph.this, line, side), line.getTerminal(side)); + } + + @Override + public void visitThreeWindingsTransformer(ThreeWindingsTransformer transformer, + ThreeWindingsTransformer.Side side) { + addFeeder(Feeder3WTNode.create(Graph.this, transformer, side), transformer.getTerminal(side)); + } + } + + private class NodeBreakerGraphBuilder extends AbstractGraphBuilder { + + private final Map nodesByNumber; + + NodeBreakerGraphBuilder(Map nodesByNumber) { + this.nodesByNumber = Objects.requireNonNull(nodesByNumber); + } + + public ConnectablePosition.Feeder getFeeder(Terminal terminal) { + Connectable connectable = terminal.getConnectable(); + ConnectablePosition position = (ConnectablePosition) connectable.getExtension(ConnectablePosition.class); + if (position == null) { + return null; + } + if (connectable instanceof Injection) { + return position.getFeeder(); + } else if (connectable instanceof Branch) { + Branch branch = (Branch) connectable; + if (branch.getTerminal1() == terminal) { + return position.getFeeder1(); + } else if (branch.getTerminal2() == terminal) { + return position.getFeeder2(); + } else { + throw new AssertionError(); + } + } else if (connectable instanceof ThreeWindingsTransformer) { + ThreeWindingsTransformer twt = (ThreeWindingsTransformer) connectable; + if (twt.getLeg1().getTerminal() == terminal) { + return position.getFeeder1(); + } else if (twt.getLeg2().getTerminal() == terminal) { + return position.getFeeder2(); + } else if (twt.getLeg3().getTerminal() == terminal) { + return position.getFeeder3(); + } else { + throw new AssertionError(); + } + } else { + throw new AssertionError(); + } + } + + protected void addFeeder(FeederNode node, Terminal terminal) { + ConnectablePosition.Feeder feeder = getFeeder(terminal); + if (feeder != null) { + node.setOrder(feeder.getOrder()); + node.setLabel(feeder.getName()); + node.setDirection(BusCell.Direction.valueOf(feeder.getDirection().toString())); + } + nodesByNumber.put(terminal.getNodeBreakerView().getNode(), node); + addNode(node); + } + + @Override + public void visitBusbarSection(BusbarSection busbarSection) { + BusbarSectionPosition extension = busbarSection.getExtension(BusbarSectionPosition.class); + BusNode node = BusNode.create(Graph.this, busbarSection); + if (extension != null) { + node.setStructuralPosition(new Position(extension.getSectionIndex(), extension.getBusbarIndex()) + .setHSpan(1)); + } + nodesByNumber.put(busbarSection.getTerminal().getNodeBreakerView().getNode(), node); + addNode(node); + } + } + + private class BusBreakerGraphBuilder extends AbstractGraphBuilder { + + private final Map nodesByBusId; + + private int order = 1; + + BusBreakerGraphBuilder(Map nodesByBusId) { + this.nodesByBusId = Objects.requireNonNull(nodesByBusId); + } + + protected void addFeeder(FeederNode node, Terminal terminal) { + node.setOrder(order++); + node.setDirection(order % 2 == 0 ? BusCell.Direction.TOP : BusCell.Direction.BOTTOM); + addNode(node); + SwitchNode nodeSwitch = SwitchNode.create(Graph.this, terminal); + addNode(nodeSwitch); + String busId = terminal.getBusBreakerView().getConnectableBus().getId(); + addEdge(nodesByBusId.get(busId), nodeSwitch); + addEdge(nodeSwitch, node); + } + } + + private void buildBusBreakerGraph(VoltageLevel vl) { + Map nodesByBusId = new HashMap<>(); + + int v = 1; + for (Bus b : vl.getBusBreakerView().getBuses()) { + BusNode busNode = BusNode.create(this, b); + nodesByBusId.put(b.getId(), busNode); + busNode.setStructuralPosition(new Position(1, v++)); + addNode(busNode); + } + + // visit equipments + vl.visitEquipments(new BusBreakerGraphBuilder(nodesByBusId)); + } + + private void buildNodeBreakerGraph(VoltageLevel vl) { + Map nodesByNumber = new HashMap<>(); + + // visit equipments + vl.visitEquipments(new NodeBreakerGraphBuilder(nodesByNumber)); + + // switches + for (Switch sw : vl.getNodeBreakerView().getSwitches()) { + SwitchNode n = SwitchNode.create(Graph.this, sw); + + int node1 = vl.getNodeBreakerView().getNode1(sw.getId()); + int node2 = vl.getNodeBreakerView().getNode2(sw.getId()); + + ensureNodeExists(node1, nodesByNumber); + ensureNodeExists(node2, nodesByNumber); + + addEdge(nodesByNumber.get(node1), n); + addEdge(n, nodesByNumber.get(node2)); + addNode(n); + } + + // internal connections + vl.getNodeBreakerView().getInternalConnectionStream().forEach(internalConnection -> { + int node1 = internalConnection.getNode1(); + int node2 = internalConnection.getNode2(); + + ensureNodeExists(node1, nodesByNumber); + ensureNodeExists(node2, nodesByNumber); + + addEdge(nodesByNumber.get(node1), nodesByNumber.get(node2)); + }); + } + + private void buildGraph(VoltageLevel vl) { + LOGGER.info("Building '{}' graph...", vl.getId()); + + switch (vl.getTopologyKind()) { + case BUS_BREAKER: + buildBusBreakerGraph(vl); + break; + case NODE_BREAKER: + buildNodeBreakerGraph(vl); + break; + default: + throw new AssertionError("Unknown topology kind: " + vl.getTopologyKind()); + } + + LOGGER.info("{} nodes, {} edges", nodes.size(), edges.size()); + + constructCellForThreeWindingsTransformer(); + + handleGraphPostProcessors(); + + handleConnectedComponents(); + } + + public void removeUnnecessaryFictitiousNodes() { + List fictitiousNodesToRemove = nodes.stream() + .filter(node -> node.getType() == Node.NodeType.FICTITIOUS) + .collect(Collectors.toList()); + for (Node n : fictitiousNodesToRemove) { + if (n.getAdjacentEdges().size() == 2) { + List adjNodes = n.getAdjacentNodes(); + Node node1 = adjNodes.get(0); + Node node2 = adjNodes.get(1); + LOGGER.info("Remove fictitious node {} between {} and {}", n.getId(), node1.getId(), node2.getId()); + removeNode(n); + addEdge(node1, node2); + } else { + LOGGER.info("Working on fictitious node {} with {} adjacent nodes", n.getId(), n.getAdjacentNodes().size()); + Node busNode = n.getAdjacentNodes().stream().filter(node -> node.getType() == Node.NodeType.BUS).findFirst().orElse(null); + if (busNode != null) { + n.getAdjacentNodes().stream().filter(node -> !node.equals(busNode)).forEach(node -> { + LOGGER.info("Connecting {} to {}", node.getId(), busNode.getId()); + addEdge(node, busNode); + }); + LOGGER.info("Remove fictitious node {}", n.getId()); + removeNode(n); + } else { + LOGGER.warn("Cannot remove fictitious node {} because there are no adjacent BUS nodes", n.getId()); + } + } + } + } + + public void removeFictitiousSwitchNodes() { + List fictitiousSwithcNodesToRemove = nodes.stream() + .filter(node -> node.getType() == Node.NodeType.SWITCH) + .filter(this::isFictitiousSwitchNode) + .filter(node -> node.getAdjacentNodes().size() == 2) + .collect(Collectors.toList()); + for (Node n : fictitiousSwithcNodesToRemove) { + Node node1 = n.getAdjacentNodes().get(0); + Node node2 = n.getAdjacentNodes().get(1); + LOGGER.info("Remove fictitious switch node between {} and {}", node1.getId(), node2.getId()); + removeNode(n); + addEdge(node1, node2); + } + } + + private boolean isFictitiousSwitchNode(Node node) { + Switch sw = TopologyKind.NODE_BREAKER.equals(voltageLevel.getTopologyKind()) ? + voltageLevel.getNodeBreakerView().getSwitch(node.getId()) : + voltageLevel.getBusBreakerView().getSwitch(node.getId()); + return sw == null || sw.isFictitious(); + } + + private void ensureNodeExists(int n, Map nodesByNumber) { + if (!nodesByNumber.containsKey(n)) { + FictitiousNode node = new FictitiousNode(Graph.this, "" + n); + nodesByNumber.put(n, node); + addNode(node); + } + } + + public void logCellDetectionStatus() { + Set cellsLog = new HashSet<>(); + Map cellCountByType = new EnumMap<>(Cell.CellType.class); + for (Cell.CellType cellType : Cell.CellType.values()) { + cellCountByType.put(cellType, 0); + } + int remainingNodeCount = 0; + Map remainingNodeCountByType = new EnumMap<>(Node.NodeType.class); + for (Node.NodeType nodeType : Node.NodeType.values()) { + remainingNodeCountByType.put(nodeType, 0); + } + for (Node node : nodes) { + Cell cell = node.getCell(); + if (cell != null) { + if (cellsLog.add(cell)) { + cellCountByType.put(cell.getType(), cellCountByType.get(cell.getType()) + 1); + } + } else { + remainingNodeCount++; + remainingNodeCountByType.put(node.getType(), remainingNodeCountByType.get(node.getType()) + 1); + } + } + if (cellsLog.isEmpty()) { + LOGGER.warn("No cell detected"); + } else { + LOGGER.info("{} cells detected ({})", cellsLog.size(), cellCountByType); + } + if (remainingNodeCount > 0) { + LOGGER.warn("{}/{} nodes not associated to a cell ({})", + remainingNodeCount, nodes.size(), remainingNodeCountByType); + } + } + + private UndirectedGraph toJgrapht() { + UndirectedGraph graph = new Pseudograph<>(Edge.class); + for (Node node : nodes) { + graph.addVertex(node); + } + for (Edge edge : edges) { + graph.addEdge(edge.getNode1(), edge.getNode2(), edge); + } + return graph; + } + + /** + * Check if the graph is connected or not + * + * @return true if connected, false otherwise + */ + private void handleConnectedComponents() { + List> connectedSets = new ConnectivityInspector<>(toJgrapht()).connectedSets(); + if (connectedSets.size() != 1) { + LOGGER.warn("{} connected components found", connectedSets.size()); + connectedSets.stream() + .sorted(Comparator.comparingInt(Set::size)) + .map(setNodes -> setNodes.stream().map(Node::getId).collect(Collectors.toSet())) + .forEach(strings -> LOGGER.warn(" - {}", strings)); + } + connectedSets.forEach(this::ensureOneBusInConnectedComponent); + } + + private void ensureOneBusInConnectedComponent(Set nodes) { + if (nodes.stream().anyMatch(node -> node.getType() == Node.NodeType.BUS)) { + return; + } + FictitiousNode biggestFn = nodes.stream() + .filter(node -> node.getType() == Node.NodeType.FICTITIOUS) + .sorted(Comparator.comparingInt(node -> node.getAdjacentEdges().size()) + .reversed() + .thenComparing(Node::getId)) // for stable fictitious node selection, also sort on id + .map(FictitiousNode.class::cast) + .findFirst() + .orElseThrow(() -> new PowsyblException("Empty node set")); + BusNode bn = BusNode.createFictitious(this, biggestFn.getId() + "FictitiousBus"); + addNode(bn); + substitueNode(biggestFn, bn); + } + + public void addNode(Node node) { + nodes.add(node); + nodesByType.computeIfAbsent(node.getType(), nodeType -> new ArrayList<>()).add(node); + nodesById.put(node.getId(), node); + } + + private void removeNode(Node node) { + nodes.remove(node); + nodesByType.computeIfAbsent(node.getType(), nodeType -> new ArrayList<>()).remove(node); + nodesById.remove(node.getId()); + for (Edge edge : new ArrayList<>(node.getAdjacentEdges())) { + removeEdge(edge); + } + } + + public Node getNode(String id) { + Objects.requireNonNull(id); + return nodesById.get(id); + } + + /** + * Add an edge between the two nodes + * + * @param n1 first node + * @param n2 second node + */ + public void addEdge(Node n1, Node n2) { + Edge edge = new Edge(n1, n2); + edges.add(edge); + n1.addAdjacentEdge(edge); + n2.addAdjacentEdge(edge); + } + + /** + * Remove an edge between two nodes + * + * @param n1 first node + * @param n2 second node + */ + void removeEdge(Node n1, Node n2) { + for (Edge edge : edges) { + if ((edge.getNode1().equals(n1) && edge.getNode2().equals(n2)) + || (edge.getNode1().equals(n2) && edge.getNode2().equals(n1))) { + removeEdge(edge); + return; + } + } + } + + void removeEdge(Edge edge) { + edge.getNode1().removeAdjacentEdge(edge); + edge.getNode2().removeAdjacentEdge(edge); + edges.remove(edge); + } + + /** + * Resolve when one EQ is connected with 2 switchs component + */ + + private void rIdentifyConnexComponent(Node node, List nodesIn, List connexComponent) { + if (!connexComponent.contains(node)) { + connexComponent.add(node); + List nodesToVisit = node.getAdjacentNodes() + .stream() + .filter(nodesIn::contains) + .collect(Collectors.toList()); + for (Node n : nodesToVisit) { + rIdentifyConnexComponent(n, nodesIn, connexComponent); + } + } + } + + public List> getConnexComponents(List nodesIn) { + List nodesToHandle = new ArrayList<>(nodesIn); + List> result = new ArrayList<>(); + while (!nodesToHandle.isEmpty()) { + Node n = nodesToHandle.get(0); + List connexComponent = new ArrayList<>(); + rIdentifyConnexComponent(n, nodesIn, connexComponent); + nodesToHandle.removeAll(connexComponent); + result.add(connexComponent); + } + return result; + } + + public List signatureSortedCellsContent() { + return cells.stream().map(Cell::getFullId).sorted().collect(Collectors.toList()); + } + + public boolean compareCellDetection(Graph graph) { + return signatureSortedCellsContent().equals(graph.signatureSortedCellsContent()); + } + + public Position getMaxBusStructuralPosition() { + return maxBusStructuralPosition; + } + + public void setMaxBusPosition() { + List h = new ArrayList<>(); + List v = new ArrayList<>(); + getNodeBuses().forEach(nodeBus -> { + v.add(nodeBus.getStructuralPosition().getV()); + h.add(nodeBus.getStructuralPosition().getH()); + }); + if (h.isEmpty() || v.isEmpty()) { + return; + } + maxBusStructuralPosition.setH(Collections.max(h)); + maxBusStructuralPosition.setV(Collections.max(v)); + } + + public Stream getBusCells() { + return cells.stream() + .filter(cell -> cell instanceof BusCell && !((BusCell) cell).getPrimaryLegBlocks().isEmpty()) + .map(BusCell.class::cast); + } + + private void buildVPosToHposToNodeBus() { + vPosToHPosToNodeBus = new HashMap<>(); + getNodeBuses() + .forEach(nodeBus -> { + int vPos = nodeBus.getStructuralPosition().getV(); + int hPos = nodeBus.getStructuralPosition().getH(); + vPosToHPosToNodeBus.putIfAbsent(vPos, new HashMap<>()); + vPosToHPosToNodeBus.get(vPos).put(hPos, nodeBus); + }); + } + + public void extendFeederWithMultipleSwitches() { + List nodesToAdd = new ArrayList<>(); + for (Node n : nodes) { + if (n instanceof FeederNode && n.getAdjacentNodes().size() > 1) { + // Create a new fictitious node + FictitiousNode nf = new FictitiousNode(Graph.this, n.getId() + "Fictif"); + nodesToAdd.add(nf); + // Create all new edges and remove old ones + List oldNeighboor = new ArrayList<>(n.getAdjacentNodes()); + for (Node neighboor : oldNeighboor) { + addEdge(nf, neighboor); + removeEdge(n, neighboor); + } + addEdge(n, nf); + } + } + nodes.addAll(nodesToAdd); + } + + //add a fictitious node between 2 switches or between a switch and a feeder + //when one switch is connected to a bus + public void extendFirstOutsideNode() { + getNodeBuses().stream() + .flatMap(node -> node.getAdjacentNodes().stream()) + .filter(node -> node.getType() == Node.NodeType.SWITCH) + .forEach(nodeSwitch -> + nodeSwitch.getAdjacentNodes().stream() + .filter(node -> node.getType() == Node.NodeType.SWITCH || + node.getType() == Node.NodeType.FEEDER) + .forEach(node -> { + removeEdge(node, nodeSwitch); + FictitiousNode newNode = new FictitiousNode(Graph.this, nodeSwitch.getId() + "Fictif"); + addNode(newNode); + addEdge(node, newNode); + addEdge(nodeSwitch, newNode); + })); + } + + //the first element shouldn't be a Breaker + public void extendBreakerConnectedToBus() { + getNodeBuses().forEach(nodeBus -> nodeBus.getAdjacentNodes().stream() + .filter(node -> node.getType() == Node.NodeType.SWITCH + && ((SwitchNode) node).getKind() != SwitchKind.DISCONNECTOR) + .forEach(nodeSwitch -> addDoubleNode(nodeBus, nodeSwitch, ""))); + } + + //the first element shouldn't be a Feeder + public void extendFeederConnectedToBus() { + getNodeBuses().forEach(nodeBus -> nodeBus.getAdjacentNodes().stream() + .filter(node -> node.getType() == Node.NodeType.FEEDER) + .forEach(feeder -> addDoubleNode(nodeBus, feeder, ""))); + } + + public void extendSwitchBetweenBus(SwitchNode nodeSwitch) { + List copyAdj = new ArrayList<>(nodeSwitch.getAdjacentNodes()); + addDoubleNode((BusNode) copyAdj.get(0), nodeSwitch, "0"); + addDoubleNode((BusNode) copyAdj.get(1), nodeSwitch, "1"); + } + + private void addDoubleNode(BusNode busNode, Node node, String suffix) { + removeEdge(busNode, node); + SwitchNode fNodeToBus = SwitchNode.createFictitious(Graph.this, node.getId() + "fSwitch" + suffix, node.isOpen()); + addNode(fNodeToBus); + FictitiousNode fNodeToSw = new FictitiousNode(Graph.this, node.getId() + "fNode" + suffix); + addNode(fNodeToSw); + addEdge(busNode, fNodeToBus); + addEdge(fNodeToBus, fNodeToSw); + addEdge(fNodeToSw, node); + } + + private void substitueNode(Node nodeOrigin, Node newNode) { + while (!nodeOrigin.getAdjacentEdges().isEmpty()) { + Edge edge = nodeOrigin.getAdjacentEdges().get(0); + Node node1 = edge.getNode1() == nodeOrigin ? newNode : edge.getNode1(); + Node node2 = edge.getNode2() == nodeOrigin ? newNode : edge.getNode2(); + addEdge(node1, node2); + removeEdge(edge); + } + removeNode(nodeOrigin); + } + + public void substituteFictitiousNodesMirroringBusNodes() { + getNodeBuses().forEach(busNode -> { + List adjs = busNode.getAdjacentNodes(); + if (adjs.size() == 1 && adjs.get(0).getType() == Node.NodeType.FICTITIOUS) { + Node adj = adjs.get(0); + removeEdge(adj, busNode); + substitueNode(adj, busNode); + } + }); + } + + public void substituteSingularFictitiousByFeederNode() { + getNodes().stream() + .filter(n -> n.getType() == Node.NodeType.FICTITIOUS && n.getAdjacentEdges().size() == 1) + .forEach(n -> { + FeederNode feederNode = FeederNode.createFictitious(this, n.getId()); + addNode(feederNode); + substitueNode(n, feederNode); + }); + } + + public BusNode getVHNodeBus(int v, int h) { + if (vPosToHPosToNodeBus == null) { + buildVPosToHposToNodeBus(); + } + if (!vPosToHPosToNodeBus.containsKey(v)) { + return null; + } + if (!vPosToHPosToNodeBus.get(v).containsKey(h)) { + return null; + } + return vPosToHPosToNodeBus.get(v).get(h); + } + + public void addCell(Cell c) { + cells.add(c); + } + + public void removeCell(Cell c) { + cells.remove(c); + } + + public List getNodeBuses() { + return nodesByType.computeIfAbsent(Node.NodeType.BUS, nodeType -> new ArrayList<>()) + .stream() + .map(BusNode.class::cast) + .collect(Collectors.toList()); + } + + public List getNodes() { + return new ArrayList<>(nodes); + } + + public List getEdges() { + return new ArrayList<>(edges); + } + + public Set getCells() { + return new TreeSet<>(cells); + } + + public VoltageLevel getVoltageLevel() { + return voltageLevel; + } + + public double getX() { + return x; + } + + public void setX(double x) { + this.x = x; + } + + public double getY() { + return y; + } + + public void setY(double y) { + this.y = y; + } + + public void writeJson(Path file) { + try (Writer writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { + writeJson(writer); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public void writeJson(Writer writer) { + Objects.requireNonNull(writer); + try (JsonGenerator generator = new JsonFactory() + .createGenerator(writer) + .useDefaultPrettyPrinter()) { + generator.writeStartArray(); + for (Cell cell : cells) { + cell.writeJson(generator); + } + generator.writeEndArray(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private class InfosInductor3WT { + private final String id; // id of the inductor + private final String name; // name of the inductor + private final ThreeWindingsTransformer.Side side; // side of the 3WT where the inductor is found + + InfosInductor3WT(String id, String name, ThreeWindingsTransformer.Side side) { + this.id = id; + this.name = name; + this.side = side; + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public ThreeWindingsTransformer.Side getSide() { + return side; + } + } + + /* + * Finding the inductor, if present, in the tertiary voltage level of a 3 windings transformer + */ + private InfosInductor3WT findInductorOf3WT(Feeder3WTNode node) { + final AtomicReference infos = new AtomicReference<>(); + + ThreeWindingsTransformer transformer = node.getTransformer(); + + transformer.getTerminals().stream() + .filter(t -> transformer.getSide(t) != ThreeWindingsTransformer.Side.ONE) + .forEach(t -> { + VoltageLevel v = t.getVoltageLevel(); + if (v != node.getGraph().getVoltageLevel()) { + v.getShuntCompensatorStream().forEach(s -> { + Bus connectableBusInductor = s.getTerminal().getBusView().getConnectableBus(); + Bus connectableBus3WT = t.getBusView().getConnectableBus(); + if (connectableBusInductor == connectableBus3WT && + s.getbPerSection() < 0 && infos.get() == null) { // inductor found + infos.set(new InfosInductor3WT(s.getId(), s.getName(), transformer.getSide(t))); + } + }); + } + }); + + return infos.get(); + } + + private void constructCellForThreeWindingsTransformer() { + getNodes().stream() + .filter(n -> n instanceof Feeder3WTNode) + .forEach(n -> { + Feeder3WTNode n3WT = (Feeder3WTNode) n; + + // Create a new fictitious node + Fictitious3WTNode nf = new Fictitious3WTNode(this, n3WT.getLabel() + "_fictif", n3WT.getTransformer()); + addNode(nf); + + FeederNode nfeeder1 = null; + FeederNode nfeeder2 = null; + + InfosInductor3WT infosInductor = null; + if (showInductorFor3WT && forVoltageLevelDiagram) { + // finding the inductor in the tertiary voltage level + infosInductor = findInductorOf3WT(n3WT); + } + + if (infosInductor != null) { + // We are in a voltage level diagram AND + // We want to show (if we are not in the tertiary voltage level), + // the inductor present in the tertiary voltage level of the 3 windings transformer, + + // We represent the 3 windings transformer like a double feeder cell with : + // . one winding to the secondary voltage level + // . one feeder for the inductor present in the tertiary voltage level + + if (infosInductor.getSide() == ThreeWindingsTransformer.Side.TWO) { + // Create a feeder for the inductor + String idFeeder1 = infosInductor.getId() + "_" + n3WT.getTransformer().getId(); + String nameFeeder1 = infosInductor.getName(); + nfeeder1 = Feeder2WTNode.create(Graph.this, idFeeder1, nameFeeder1, n3WT.getVL2()); + nfeeder1.setComponentType(INDUCTOR); + } else { + // Create a feeder for the winding to the secondary voltage level + String idFeeder1 = n3WT.getId() + "_" + n3WT.getId2(); + String nameFeeder1 = n3WT.getName() + "_" + n3WT.getName2(); + nfeeder1 = Feeder2WTNode.create(Graph.this, idFeeder1, nameFeeder1, n3WT.getVL2()); + nfeeder1.setComponentType(LINE); + } + nfeeder1.setOrder(n3WT.getOrder()); + nfeeder1.setDirection(n3WT.getDirection()); + addNode(nfeeder1); + + if (infosInductor.getSide() == ThreeWindingsTransformer.Side.THREE) { + // Create a feeder for the inductor + String idFeeder2 = infosInductor.getId() + "_" + n3WT.getTransformer().getId(); + String nameFeeder2 = infosInductor.getName(); + nfeeder2 = Feeder2WTNode.create(Graph.this, idFeeder2, nameFeeder2, n3WT.getVL3()); + nfeeder2.setComponentType(INDUCTOR); + } else { + // Create a feeder for the winding to the tertiary voltage level + String idFeeder2 = n3WT.getId() + "_" + n3WT.getId3(); + String nameFeeder2 = n3WT.getName() + "_" + n3WT.getName3(); + nfeeder2 = Feeder2WTNode.create(Graph.this, idFeeder2, nameFeeder2, n3WT.getVL3()); + nfeeder2.setComponentType(LINE); + } + nfeeder2.setOrder(n3WT.getOrder() + 1); + nfeeder2.setDirection(n3WT.getDirection()); + addNode(nfeeder2); + } else { + // We represent the 3 windings transformer like a double feeder cell with : + // . one winding to the first other voltage level + // . one winding to the second other voltage level + + // Create a feeder for the winding to the first other voltage level + String idFeeder1 = n3WT.getId() + "_" + n3WT.getId2(); + String nameFeeder1 = n3WT.getName() + "_" + n3WT.getName2(); + nfeeder1 = Feeder2WTNode.create(Graph.this, idFeeder1, nameFeeder1, n3WT.getVL2()); + nfeeder1.setComponentType(LINE); + nfeeder1.setOrder(n3WT.getOrder()); + nfeeder1.setDirection(n3WT.getDirection()); + addNode(nfeeder1); + + // Create a feeder for the winding to the second other voltage level + String idFeeder2 = n3WT.getId() + "_" + n3WT.getId3(); + String nameFeeder2 = n3WT.getName() + "_" + n3WT.getName3(); + nfeeder2 = Feeder2WTNode.create(Graph.this, idFeeder2, nameFeeder2, n3WT.getVL3()); + nfeeder2.setComponentType(LINE); + nfeeder2.setOrder(n3WT.getOrder() + 1); + nfeeder2.setDirection(n3WT.getDirection()); + addNode(nfeeder2); + } + + // Replacement of the old 3WT feeder node by the new fictitious node + substitueNode(n3WT, nf); + + // Add edges between the new fictitious node and the new feeder nodes + addEdge(nf, nfeeder1); + addEdge(nf, nfeeder2); + }); + } + + /** + Discover and apply postprocessor plugins to add custom nodes + **/ + private void handleGraphPostProcessors() { + List listPostProcessors = POST_PROCESSOR_LOADER.getServices(); + for (GraphBuildPostProcessor gbp : listPostProcessors) { + LOGGER.info("Graph post-processor id '{}' : Adding custom node in graph '{}'", + gbp.getId(), voltageLevel.getId()); + gbp.addNode(this); + } + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/InternCell.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/InternCell.java new file mode 100644 index 000000000..06414bbaa --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/InternCell.java @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.sld.layout.LayoutParameters; + +import java.util.*; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class InternCell extends AbstractBusCell { + + private Map legs; + private Block body; + private int hSpan; + private static Side BODYSIDE = Side.LEFT; + + public InternCell(Graph graph) { + super(graph, CellType.INTERN); + setDirection(Direction.TOP); + } + + public void organizeBlocks() { + legs = new EnumMap<>(Side.class); + List candidateLegs = searchLegs(); + if (getRootBlock().getType() == Block.Type.SERIAL && candidateLegs.size() == 2) { + SerialBlock serialRootBlock = (SerialBlock) getRootBlock(); + assignLeg(serialRootBlock, candidateLegs.get(0)); + assignLeg(serialRootBlock, candidateLegs.get(1)); + body = serialRootBlock.extractBody(new ArrayList<>(legs.values())); + body.setOrientation(Orientation.HORIZONTAL); + } else { // if (candidateLegs.size() == 1) { TODO: to choose ? + legs.put(Side.UNDEFINED, candidateLegs.get(0)); +// } else { +// throw new PowsyblException("InternCell pattern not recognized"); + } + } + + private void assignLeg(SerialBlock sb, LegBlock candidateLeg) { + Block.Extremity extremity = sb.whichExtremity(candidateLeg); + if (extremity != Block.Extremity.NONE) { + legs.put(extremityToSide(extremity), candidateLeg); + } else { + throw new PowsyblException("Unable to identify legs of internCell"); + } + } + + private Side extremityToSide(Block.Extremity extremity) { + if (extremity == Block.Extremity.START) { + return Side.LEFT; + } + if (extremity == Block.Extremity.END) { + return Side.RIGHT; + } + return Side.UNDEFINED; + } + + private List searchLegs() { + List candidateLegs = new ArrayList<>(); + List plbCopy = new ArrayList<>(getPrimaryLegBlocks()); + while (!plbCopy.isEmpty()) { + LegPrimaryBlock lpb = plbCopy.get(0); + Block parentBlock = lpb.getParentBlock(); + if (parentBlock instanceof LegParralelBlock) { + candidateLegs.add((LegBlock) parentBlock); + plbCopy.removeAll(((LegParralelBlock) parentBlock).subBlocks); + } else { + candidateLegs.add(lpb); + plbCopy.remove(lpb); + } + } + return candidateLegs; + } + + public void postPositioningSettings() { + identifyIfFlat(); + } + + private void identifyIfFlat() { + List buses = getBusNodes(); + if (buses.size() != 2) { + return; + } + Position pos1 = buses.get(0).getStructuralPosition(); + Position pos2 = buses.get(1).getStructuralPosition(); + if (Math.abs(pos2.getH() - pos1.getH()) == 1 && pos2.getV() == pos1.getV()) { + setDirection(Direction.FLAT); + getRootBlock().setOrientation(Orientation.HORIZONTAL); + } + } + + public boolean isFlat() { + return getDirection() == Direction.FLAT; + } + + public boolean isUniLeg() { + return legs.containsKey(Side.UNDEFINED); + } + + public void reverseCell() { + body.reverseBlock(); + if (legs.get(Side.LEFT) != null) { + LegBlock tmp = legs.get(Side.LEFT); + legs.put(Side.LEFT, legs.get(Side.RIGHT)); + legs.put(Side.RIGHT, tmp); + } + } + + public int getSideHPos(Side side) { + return getSideToLeg(side).getPosition().getH(); + } + + @Override + public void blockSizing() { + legs.values().forEach(Block::sizing); + if (!isUniLeg()) { + body.sizing(); + } + } + + @Override + public int newHPosition(int hPosition) { + int h = hPosition; + if (isUniLeg()) { + legs.get(Side.UNDEFINED).getPosition().setH(h); + h += legs.get(Side.UNDEFINED).getPosition().getHSpan(); + } else { + legs.get(Side.LEFT).getPosition().setH(h); + h += legs.get(Side.LEFT).getPosition().getHSpan(); + if (isFlat()) { + body.getPosition().setHV(h, legs.get(Side.LEFT).getBusNodes().get(0).getStructuralPosition().getV()); + } else { + h -= 1; + body.getPosition().setHV(h, 1); + } + h += body.getPosition().getHSpan(); + legs.get(Side.RIGHT).getPosition().setH(h); + h += legs.get(Side.RIGHT).getPosition().getHSpan(); + } + return h; + } + + public int newHPosition(int hPosition, Side side) { + int h = hPosition; + if (side == Side.LEFT) { + legs.get(Side.LEFT).getPosition().setH(h); + h += legs.get(Side.LEFT).getPosition().getHSpan(); + } + if (side == BODYSIDE) { + h -= 1; + body.getPosition().setHV(h, 1); + h += body.getPosition().getHSpan(); + } + if (side == Side.RIGHT) { + legs.get(Side.RIGHT).getPosition().setH(h); + h += legs.get(Side.RIGHT).getPosition().getHSpan(); + } + return h; + } + + @Override + public void calculateCoord(LayoutParameters layoutParam) { + legs.values().forEach(lb -> lb.calculateRootCoord(layoutParam)); + if (!isUniLeg()) { + body.calculateRootCoord(layoutParam); + } + } + + public LegBlock getSideToLeg(Side side) { + return legs.get(side); + } + + public List getSideBusNodes(Side side) { + return legs.get(side).getBusNodes(); + } + + public Block getBodyBlock() { + return body; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegBlock.java new file mode 100644 index 000000000..b4bbf0642 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegBlock.java @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public interface LegBlock extends Block { + + default FictitiousNode getLegNode() { + return (FictitiousNode) getExtremityNode(Extremity.END); + } + + List getBusNodes(); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegParralelBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegParralelBlock.java new file mode 100644 index 000000000..95ce78ad4 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegParralelBlock.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.sld.layout.LayoutParameters; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class LegParralelBlock extends AbstractParallelBlock implements LegBlock { + + public LegParralelBlock(List subBlocks, Cell cell, boolean allowMerge) { + super(subBlocks, cell, allowMerge); + } + + @Override + public List getBusNodes() { + return subBlocks.stream().map(b -> ((LegPrimaryBlock) b).getBusNode()).collect(Collectors.toList()); + } + + @Override + public void sizing() { + subBlocks.forEach(Block::sizing); + if (getPosition().getOrientation() == Orientation.VERTICAL) { + getPosition().setVSpan(0); + List subBlocksCopy = new ArrayList<>(subBlocks); + int h = 0; + while (!subBlocksCopy.isEmpty()) { + Block b = subBlocksCopy.get(0); + b.getPosition().setHV(h, 0); + if (!((LegPrimaryBlock) b).getStackableBlocks().isEmpty()) { + final int finalH = h; + ((LegPrimaryBlock) b).getStackableBlocks().forEach(sb -> sb.getPosition().setHV(finalH, 0)); + h++; + subBlocksCopy.removeAll(((LegPrimaryBlock) b).getStackableBlocks()); + } else { + h += b.getPosition().getHSpan(); + } + subBlocksCopy.remove(b); + } + getPosition().setHSpan(h); + } + // case HORIZONTAL cannot happen + } + + @Override + double initX0() { + return getCoord().getX() + + (getPosition().getHSpan() == 1 ? 0 : getCoord().getXSpan() / 2); + } + + @Override + double intitXStep() { + return getPosition().getHSpan() == 1 ? 0 : getCoord().getXSpan() / getPosition().getHSpan(); + } + + @Override + public void coordHorizontalCase(LayoutParameters layoutParam) { + // case HORIZONTAL cannot happen + } + + @Override + public String toString() { + return "BodyParallelBlock(subBlocks=" + subBlocks + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegPrimaryBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegPrimaryBlock.java new file mode 100644 index 000000000..ace12ef13 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/LegPrimaryBlock.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package com.powsybl.sld.model; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.sld.layout.LayoutParameters; + +import java.util.Collections; +import java.util.List; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class LegPrimaryBlock extends AbstractPrimaryBlock implements LegBlock { + + public LegPrimaryBlock(List nodes, Cell cell) { + super(nodes, cell); + if (getExtremityNode(Extremity.END).getType() == Node.NodeType.BUS) { + super.reverseBlock(); + } + if (!checkConsistency()) { + throw new PowsyblException("LegPrimaryBlock not consistent"); + } + } + + private boolean checkConsistency() { + return nodes.size() == 3 + && nodes.get(0).getType() == Node.NodeType.BUS + && nodes.get(1).getType() == Node.NodeType.SWITCH + && (nodes.get(2).getType() == Node.NodeType.FICTITIOUS + || nodes.get(2).getType() == Node.NodeType.SHUNT); + } + + public BusNode getBusNode() { + return (BusNode) getExtremityNode(Extremity.START); + } + + @Override + public List getBusNodes() { + return Collections.singletonList(getBusNode()); + } + + private SwitchNode getSwNode() { + return (SwitchNode) nodes.get(1); + } + + // TODO : is it a clean manner ?! + @Override + public void reverseBlock() { + + } + + @Override + public void sizing() { + if (((BusCell) getCell()).getDirection() == BusCell.Direction.FLAT + || !getStackableBlocks().isEmpty()) { + getPosition().setHSpan(0); + getPosition().setVSpan(0); + } else { + getPosition().setHSpan(1); + getPosition().setVSpan(0); + } + } + + @Override + public void coordHorizontalCase(LayoutParameters layoutParam) { + InternCell cell = (InternCell) getCell(); + // TODO : set X according to the side of the block in the intern cell ! + getSwNode().setX(getCoord().getX() + getCoord().getXSpan() / 2); + getSwNode().setY(getBusNode().getY(), false, false); + getLegNode().setY(getBusNode().getY(), true, false); + } + + @Override + public void coordVerticalCase(LayoutParameters layoutParam) { + getSwNode().setX(getCoord().getX()); + getSwNode().setY(getBusNode().getY(), false, false); + + getLegNode().setX(getCoord().getX(), true); + if (getCell().getType() == Cell.CellType.INTERN && ((InternCell) getCell()).getBodyBlock() == null) { + getLegNode().setY(layoutParam.getInitialYBus() - layoutParam.getInternCellHeight()); + } + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Node.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Node.java new file mode 100644 index 000000000..b4d91b37d --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Node.java @@ -0,0 +1,277 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.fasterxml.jackson.core.JsonGenerator; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class Node implements BaseNode { + + public enum NodeType { + BUS, + FEEDER, + FICTITIOUS, + SWITCH, + SHUNT, + OTHER + } + + protected final Graph graph; + + private NodeType type; + + private final String id; + + private final String name; + + private String componentType; + + private final boolean fictitious; + + private double x = -1; + private double y = -1; + private List xs = new ArrayList<>(); + private List ys = new ArrayList<>(); + + private boolean xPriority = false; + private boolean yPriority = false; + + private Cell cell; + + private Double rotationAngle; + + private boolean open = false; + + private final List adjacentEdges = new ArrayList<>(); + + private String label; + + /** + * Constructor + */ + protected Node(NodeType type, String id, String name, String componentType, boolean fictitious, Graph graph) { + this.type = Objects.requireNonNull(type); + this.name = name; + this.componentType = Objects.requireNonNull(componentType); + this.fictitious = fictitious; + this.graph = Objects.requireNonNull(graph); + // for unicity purpose (in substation diagram), we prefix the id of the fictitious node with the voltageLevel id and "_" + String tmpId = Objects.requireNonNull(id); + if (type == NodeType.FICTITIOUS && !StringUtils.startsWith(tmpId, "FICT_" + this.graph.getVoltageLevel().getId() + "_")) { + this.id = "FICT_" + this.graph.getVoltageLevel().getId() + "_" + tmpId; + } else { + this.id = tmpId; + } + } + + public Cell getCell() { + return cell; + } + + public void setCell(Cell cell) { + this.cell = cell; + } + + public Graph getGraph() { + return graph; + } + + @Override + public String getComponentType() { + return componentType; + } + + public void setComponentType(String componentType) { + this.componentType = componentType; + } + + @Override + public Double getRotationAngle() { + return rotationAngle; + } + + public boolean isFictitious() { + return fictitious; + } + + public void setType(NodeType type) { + this.type = type; + } + + @Override + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public String getLabel() { + if (label != null) { + return label; + } else { + return graph.isUseName() ? name : id; + } + } + + public void setLabel(String label) { + this.label = label; + } + + public List getAdjacentNodes() { + return adjacentEdges.stream() + .map(edge -> edge.getNode1() == Node.this ? edge.getNode2() : edge.getNode1()) + .collect(Collectors.toList()); + } + + public List getAdjacentEdges() { + return adjacentEdges; + } + + void addAdjacentEdge(Edge e) { + adjacentEdges.add(e); + } + + void removeAdjacentEdge(Edge e) { + adjacentEdges.remove(e); + } + + public Stream getListNodeAdjInCell(Cell cell) { + return getAdjacentNodes().stream().filter(n -> cell.getNodes().contains(n)); + } + + @Override + public double getX() { + return x; + } + + public void setX(double x) { + setX(x, false, true); + } + + public void setX(double x, boolean xPriority) { + setX(x, xPriority, true); + } + + public void setX(double x, boolean xPriority, boolean addXGraph) { + double xNode = x; + if (addXGraph) { + xNode += graph.getX(); + } + + if (!this.xPriority && xPriority) { + xs.clear(); + this.xPriority = true; + } + if (this.xPriority == xPriority) { + this.x = xNode; + xs.add(xNode); + } + } + + @Override + public double getY() { + return y; + } + + public void setY(double y) { + setY(y, false, true); + } + + public void setY(double y, boolean yPriority) { + setY(y, yPriority, true); + } + + public void setY(double y, boolean yPriority, boolean addYGraph) { + double yNode = y; + if (addYGraph) { + yNode += graph.getY(); + } + + if (!this.yPriority && yPriority) { + ys.clear(); + this.yPriority = true; + } + if (this.yPriority == yPriority) { + this.y = yNode; + ys.add(yNode); + } + } + + public NodeType getType() { + return this.type; + } + + @Override + public boolean isRotated() { + return rotationAngle != null; + } + + public void setRotationAngle(Double rotationAngle) { + this.rotationAngle = rotationAngle; + } + + public boolean isOpen() { + return open; + } + + public void setOpen(boolean open) { + this.open = open; + } + + /** + * Check similarity with another node + * + * @param n the node to compare with + * @return true IF the both are the same OR they are both Busbar OR they are both EQ(but not Busbar); + * false otherwise + **/ + public boolean checkNodeSimilarity(Node n) { + return this.equals(n) + || ((similarToAFeederNode(this)) && (similarToAFeederNode(n))) + || ((this instanceof BusNode) && (n instanceof BusNode)); + } + + public boolean similarToAFeederNode(Node n) { + return (n instanceof FeederNode) + || (n.getType() == NodeType.FICTITIOUS && n.adjacentEdges.size() == 1); + } + + public void finalizeCoord() { + x = xs.stream().mapToDouble(d -> d).average().orElse(0); + y = ys.stream().mapToDouble(d -> d).average().orElse(0); + } + + public void writeJson(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + generator.writeStringField("type", type.name()); + generator.writeStringField("id", id); + if (name != null) { + generator.writeStringField("name", name); + } + generator.writeEndObject(); + } + + @Override + public String toString() { + return "Node(id='" + getId() + "' name='" + name + "', type= " + type + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Orientation.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Orientation.java new file mode 100644 index 000000000..abf5535d8 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Orientation.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public enum Orientation { + VERTICAL, + HORIZONTAL +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ParallelBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ParallelBlock.java new file mode 100644 index 000000000..03fe4328b --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ParallelBlock.java @@ -0,0 +1,13 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +/** + * @author Benoit Jeanson + */ +public interface ParallelBlock extends ComposedBlock { +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Position.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Position.java new file mode 100644 index 000000000..f1e0a5255 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Position.java @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class Position { + + private int h; + private int v; + + private int hSpan; + private int vSpan; + + //TODO remove absolute + private boolean absolute; + + private Orientation orientation; + + public Position(int h, int v, int hSpan, int vSpan, boolean absolute, Orientation orientation) { + this.h = h; + this.v = v; + this.hSpan = hSpan; + this.vSpan = vSpan; + this.absolute = absolute; + this.orientation = orientation; + } + + public Position(int h, int v) { + this(h, v, 0, 0, false, null); + } + + public Orientation getOrientation() { + return orientation; + } + + public void setOrientation(Orientation orientation) { + this.orientation = orientation; + } + + public int getH() { + return h; + } + + public Position setH(int h) { + this.h = h; + return this; + } + + public int getV() { + return v; + } + + public Position setV(int v) { + this.v = v; + return this; + } + + public Position setHV(int h, int v) { + setH(h); + setV(v); + return this; + } + + public int getHSpan() { + return hSpan; + } + + public Position setHSpan(int hSpan) { + this.hSpan = hSpan; + return this; + } + + public int getVSpan() { + return vSpan; + } + + public Position setVSpan(int vSpan) { + this.vSpan = vSpan; + return this; + } + + @Override + public int hashCode() { + return Objects.hash(h, v, hSpan, vSpan, absolute, orientation); + } + + @Override + public boolean equals(Object o) { + if (o instanceof Position) { + Position other = (Position) o; + return other.h == h + && other.v == v + && other.hSpan == hSpan + && other.vSpan == vSpan + && other.absolute == absolute + && other.orientation == orientation; + } + return false; + } + + @Override + public String toString() { + return "Position(h=" + h + ", v=" + v + ", hSpan=" + hSpan + ", vSpan=" + vSpan + ", absolute=" + + absolute + ", orientation=" + orientation + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/PrimaryBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/PrimaryBlock.java new file mode 100644 index 000000000..aeba4a44b --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/PrimaryBlock.java @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.List; + +/** + * @author Benoit Jeanson + */ +public interface PrimaryBlock extends Block { + + List getNodes(); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/SerialBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/SerialBlock.java new file mode 100644 index 000000000..95f21da2f --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/SerialBlock.java @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.powsybl.sld.layout.LayoutParameters; + +import java.io.IOException; +import java.util.*; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class SerialBlock extends AbstractComposedBlock { + + /** + * Constructor + * A layout.block chain is oriented in order to have. + * Lower - embedding BusNode if only one of both layout.block embed a BusNode + * Upper - (as a consequence) can embed a BusNode only if Lower as one + */ + + public SerialBlock(List blocks, Cell cell) { + super(Type.SERIAL, blocks); + subBlocks = new ArrayList<>(blocks); + setCell(cell); + postConstruct(); + } + + public SerialBlock(Block block, Cell cell) { + this(Collections.singletonList(block), cell); + } + + public SerialBlock(Block block1, + Block block2, + Cell cell) { + this(Arrays.asList(block1, block2), cell); + } + + public SerialBlock(Block block1, + Block block2) { + this(Arrays.asList(block1, block2), null); + } + + private void postConstruct() { + for (int i = 0; i < subBlocks.size() - 1; i++) { + consistentChaining(subBlocks.get(i), subBlocks.get(i + 1)); + } + + if (getLowerBlock().isEmbedingNodeType(Node.NodeType.FEEDER) + || getUpperBlock().isEmbedingNodeType(Node.NodeType.BUS)) { + reverseBlock(); + } + +// TODO shouldn't it be this way only in the Vertical case ??? + for (int i = 0; i < subBlocks.size(); i++) { + subBlocks.get(i).getPosition().setHV(0, i); + } + + setCardinality(Extremity.START, getLowerBlock().getCardinality(Extremity.START)); + setCardinality(Extremity.END, getUpperBlock().getCardinality(Extremity.END)); + } + + private void consistentChaining(Block block1, Block block2) { + if (block1.getExtremityNode(Extremity.END) == block2.getExtremityNode(Extremity.START)) { + return; + } + if (block1.getExtremityNode(Extremity.END) == block2.getExtremityNode(Extremity.END)) { + block2.reverseBlock(); + return; + } + if (block1.getExtremityNode(Extremity.START) == block2.getExtremityNode(Extremity.END)) { + block1.reverseBlock(); + block2.reverseBlock(); + return; + } + if (block1.getExtremityNode(Extremity.START) == block2.getExtremityNode(Extremity.START)) { + block1.reverseBlock(); + } + } + + public boolean addSubBlock(Block block) { + for (Extremity myExtremity : Extremity.values()) { + if (getExtremityNode(myExtremity) instanceof FictitiousNode) { + FictitiousNode commonNode = (FictitiousNode) getExtremityNode(myExtremity); + for (Extremity itsExtremity : Extremity.values()) { + if (commonNode == block.getExtremityNode(itsExtremity) + && commonNode.getCardinality() == getCardinality(commonNode) + block.getCardinality(commonNode) + ) { + insertBlock(block, myExtremity); + return true; + } + } + } + } + return false; + } + + Extremity whichExtremity(Block block) { + if (block.equals(subBlocks.get(0))) { + return Extremity.START; + } + if (block.equals(subBlocks.get(subBlocks.size() - 1))) { + return Extremity.END; + } + return Extremity.NONE; + } + + Block extractBody(Collection blocks) { + List subBlocksCopy = new ArrayList<>(subBlocks); + subBlocksCopy.removeAll(blocks); + if (subBlocksCopy.size() == 1) { + return new BodyPrimaryBlock((BodyPrimaryBlock) subBlocksCopy.get(0)); + } else { + return new SerialBlock(subBlocksCopy, getCell()); + } + } + + private void insertBlock(Block block, Extremity myExtremity) { + block.setParentBlock(this); + if (myExtremity == Extremity.START) { + subBlocks.add(0, block); + } else { + subBlocks.add(block); + } + postConstruct(); + } + + public Block getUpperBlock() { + return subBlocks.get(subBlocks.size() - 1); + } + + public Block getLowerBlock() { + return subBlocks.get(0); + } + + @Override + public void sizing() { + subBlocks.forEach(Block::sizing); + if (getPosition().getOrientation() == Orientation.VERTICAL) { + getPosition().setHSpan(subBlocks.stream().mapToInt(block -> block.getPosition().getHSpan()).max().orElse(0)); + getPosition().setVSpan(subBlocks.stream().mapToInt(block -> block.getPosition().getVSpan()).sum()); + + int cumulVSpan = 0; + for (Block subBlock : subBlocks) { + Position pos = subBlock.getPosition(); + pos.setHV(0, cumulVSpan); + cumulVSpan += pos.getVSpan(); + } + } else { + getPosition().setVSpan(subBlocks.stream().mapToInt(block -> block.getPosition().getVSpan()).max().orElse(0)); + getLowerBlock().getPosition().setHV(0, 0); + + int cumulHSpan = getLowerBlock().getPosition().getHSpan(); + for (int i = 1; i < subBlocks.size(); i++) { + subBlocks.get(i).getPosition().setHV(cumulHSpan, 0); + cumulHSpan += subBlocks.get(i).getPosition().getHSpan(); + } + getPosition().setHSpan(cumulHSpan); + } + } + + @Override + public void coordVerticalCase(LayoutParameters layoutParam) { + double y0; + double yPxStep; + int sign = ((BusCell) getCell()).getDirection() == BusCell.Direction.TOP ? 1 : -1; + y0 = getCoord().getY() + sign * getCoord().getYSpan() / 2; + yPxStep = -sign * getCoord().getYSpan() / getPosition().getVSpan(); + + for (Block sub : subBlocks) { + sub.setX(getCoord().getX()); + sub.setXSpan(getCoord().getXSpan()); + + sub.setYSpan( + getCoord().getYSpan() * ((double) sub.getPosition().getVSpan() / getPosition().getVSpan())); + sub.setY(y0 + yPxStep * (sub.getPosition().getV() + (double) sub.getPosition().getVSpan() / 2)); + + sub.calculateCoord(layoutParam); + } + } + + @Override + public void coordHorizontalCase(LayoutParameters layoutParam) { + double x0 = getCoord().getX() - getCoord().getXSpan() / 2; + double xPxStep = getCoord().getXSpan() / getPosition().getHSpan(); + double xTranslateInternalNonFlatCell = 0; + + for (int i = 0; i < subBlocks.size(); i++) { + Block sub = subBlocks.get(i); + sub.setX(x0 + (sub.getPosition().getH() + (double) sub.getPosition().getHSpan() / 2) * xPxStep + + xTranslateInternalNonFlatCell); + sub.setXSpan(sub.getPosition().getHSpan() * xPxStep); + sub.setY(getCoord().getY()); + sub.setYSpan(getCoord().getYSpan()); + sub.calculateCoord(layoutParam); + } + } + + @Override + protected void writeJsonContent(JsonGenerator generator) throws IOException { + generator.writeFieldName("blocks"); + generator.writeStartArray(); + for (Block subBlock : subBlocks) { + subBlock.writeJson(generator); + } + generator.writeEndArray(); + } + + @Override + public String toString() { + return "SerialBlock(subBlocks=" + subBlocks + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ShuntCell.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ShuntCell.java new file mode 100644 index 000000000..4dec7cff0 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/ShuntCell.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.sld.layout.LayoutParameters; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class ShuntCell extends AbstractCell { + public ShuntCell(Graph graph) { + super(graph, CellType.SHUNT); + } + + public void calculateCoord(LayoutParameters layoutParam) { + if (getRootBlock() instanceof BodyPrimaryBlock) { + ((BodyPrimaryBlock) getRootBlock()).coordShuntCase(); + } else { + throw new PowsyblException("ShuntCell can only be composed of a single BodyPrimaryBlock"); + } + } + + @Override + public String toString() { + return "ShuntCell(" + nodes + " )"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Side.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Side.java new file mode 100644 index 000000000..aaad32111 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/Side.java @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public enum Side { + UNDEFINED, + LEFT, + RIGHT; + + public Side getFlip() { + if (this == UNDEFINED) { + return UNDEFINED; + } + return this == Side.RIGHT ? Side.LEFT : Side.RIGHT; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/SubstationGraph.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/SubstationGraph.java new file mode 100644 index 000000000..9edad10d7 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/SubstationGraph.java @@ -0,0 +1,175 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.powsybl.iidm.network.Substation; +import com.powsybl.iidm.network.Terminal; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.iidm.network.VoltageLevel; + +/** + * This class builds the connectivity among the voltageLevels of a substation + * buildSubstationGraph establishes the List of nodes, edges + * + * @author Franck Lecuyer + */ +public class SubstationGraph { + + private static final Logger LOGGER = LoggerFactory.getLogger(SubstationGraph.class); + + private Substation substation; + + private final List nodes = new ArrayList<>(); + + private final List edges = new ArrayList<>(); + + private final Map nodesById = new HashMap<>(); + + /** + * Constructor + */ + public SubstationGraph(Substation substation) { + this.substation = Objects.requireNonNull(substation); + } + + public static SubstationGraph create(Substation s) { + return create(s, false); + } + + public static SubstationGraph create(Substation s, boolean useName) { + Objects.requireNonNull(s); + SubstationGraph g = new SubstationGraph(s); + g.buildSubstationGraph(useName); + return g; + } + + private void buildSubstationGraph(boolean useName) { + // building the graph for each voltageLevel (ordered by descending voltageLevel nominalV) + substation.getVoltageLevelStream() + .sorted(Comparator.comparing(VoltageLevel::getNominalV) + .reversed()).forEach(v -> { + Graph vlGraph = Graph.create(v, useName, false, false); + addNode(vlGraph); + }); + + LOGGER.info("Number of node : {} ", nodes.size()); + + // Creation of snake lines for transformers between the voltage levels + // in the substation diagram + addSnakeLines(); + } + + private void addSnakeLines() { + // Two windings transformer + // + for (TwoWindingsTransformer transfo : substation.getTwoWindingsTransformers()) { + Terminal t1 = transfo.getTerminal1(); + Terminal t2 = transfo.getTerminal2(); + + String id1 = transfo.getId() + "_" + transfo.getSide(t1).name(); + String id2 = transfo.getId() + "_" + transfo.getSide(t2).name(); + + VoltageLevel v1 = t1.getVoltageLevel(); + VoltageLevel v2 = t2.getVoltageLevel(); + + Graph g1 = getNode(v1.getId()); + Graph g2 = getNode(v2.getId()); + + Node n1 = g1.getNode(id1); + Node n2 = g2.getNode(id2); + + addEdge(n1, n2); + } + + // Three windings transformer + // + for (ThreeWindingsTransformer transfo : substation.getThreeWindingsTransformers()) { + Terminal t1 = transfo.getLeg1().getTerminal(); + Terminal t2 = transfo.getLeg2().getTerminal(); + Terminal t3 = transfo.getLeg3().getTerminal(); + + String id12 = transfo.getId() + "_" + transfo.getSide(t1).name() + "_" + transfo.getSide(t2).name(); + String id13 = transfo.getId() + "_" + transfo.getSide(t1).name() + "_" + transfo.getSide(t3).name(); + + String id21 = transfo.getId() + "_" + transfo.getSide(t2).name() + "_" + transfo.getSide(t1).name(); + String id23 = transfo.getId() + "_" + transfo.getSide(t2).name() + "_" + transfo.getSide(t3).name(); + + String id31 = transfo.getId() + "_" + transfo.getSide(t3).name() + "_" + transfo.getSide(t1).name(); + String id32 = transfo.getId() + "_" + transfo.getSide(t3).name() + "_" + transfo.getSide(t2).name(); + + VoltageLevel v1 = t1.getVoltageLevel(); + VoltageLevel v2 = t2.getVoltageLevel(); + VoltageLevel v3 = t3.getVoltageLevel(); + + Graph g1 = getNode(v1.getId()); + Graph g2 = getNode(v2.getId()); + Graph g3 = getNode(v3.getId()); + + Node n12 = g1.getNode(id12); + Node n13 = g1.getNode(id13); + + Node n21 = g2.getNode(id21); + Node n23 = g2.getNode(id23); + + Node n31 = g3.getNode(id31); + Node n32 = g3.getNode(id32); + + addEdge(n12, n21); + addEdge(n13, n31); + addEdge(n23, n32); + } + } + + public void addNode(Graph node) { + nodes.add(node); + nodesById.put(node.getVoltageLevel().getId(), node); + } + + public Graph getNode(String id) { + Objects.requireNonNull(id); + return nodesById.get(id); + } + + public void addEdge(Node n1, Node n2) { + TwtEdge sl = new TwtEdge(n1, n2); + edges.add(sl); + } + + public List getNodes() { + return new ArrayList<>(nodes); + } + + public List getEdges() { + return new ArrayList<>(edges); + } + + public boolean graphAdjacents(Graph g1, Graph g2) { + int nbNodes = nodes.size(); + for (int i = 0; i < nbNodes; i++) { + if (nodes.get(i) == g1 && i < (nbNodes - 1) && nodes.get(i + 1) == g2) { + return true; + } + } + return false; + } + + public Substation getSubstation() { + return substation; + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/SwitchNode.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/SwitchNode.java new file mode 100644 index 000000000..f29f2c2de --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/SwitchNode.java @@ -0,0 +1,81 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.network.Bus; +import com.powsybl.iidm.network.Switch; +import com.powsybl.iidm.network.SwitchKind; +import com.powsybl.iidm.network.Terminal; + +import java.util.Objects; + +import static com.powsybl.sld.library.ComponentTypeName.BREAKER; +import static com.powsybl.sld.library.ComponentTypeName.DISCONNECTOR; +import static com.powsybl.sld.library.ComponentTypeName.LOAD_BREAK_SWITCH; +import static com.powsybl.sld.library.ComponentTypeName.NODE; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class SwitchNode extends Node { + + private final SwitchKind kind; + + protected SwitchNode(String id, String name, String componentType, boolean fictitious, Graph graph, SwitchKind kind, boolean open) { + super(NodeType.SWITCH, id, name, componentType, fictitious, graph); + this.kind = Objects.requireNonNull(kind); + setOpen(open); + } + + public static SwitchNode create(Graph graph, Switch aSwitch) { + Objects.requireNonNull(graph); + Objects.requireNonNull(aSwitch); + String componentType; + switch (aSwitch.getKind()) { + case BREAKER: + componentType = BREAKER; + break; + case DISCONNECTOR: + componentType = DISCONNECTOR; + break; + case LOAD_BREAK_SWITCH: + componentType = LOAD_BREAK_SWITCH; + break; + default: + throw new AssertionError(); + } + return new SwitchNode(aSwitch.getId(), aSwitch.getName(), componentType, false, graph, aSwitch.getKind(), aSwitch.isOpen()); + } + + public static SwitchNode create(Graph graph, Terminal terminal) { + Objects.requireNonNull(graph); + Objects.requireNonNull(terminal); + Bus bus = terminal.getBusBreakerView().getConnectableBus(); + String id = bus.getId() + "_" + terminal.getConnectable().getId(); + String name = bus.getName() + "_" + terminal.getConnectable().getName(); + return new SwitchNode(id, name, DISCONNECTOR, false, graph, SwitchKind.DISCONNECTOR, !terminal.isConnected()); + } + + public static SwitchNode createFictitious(Graph graph, String id, boolean open) { + return new SwitchNode(id, id, NODE, true, graph, SwitchKind.BREAKER, open); + } + + public SwitchKind getKind() { + return kind; + } + + public Node getOtherAdjNode(Node adj) { + // a switch node has 2 and only 2 adjacent nodes. + if (getAdjacentNodes().size() != 2) { + throw new PowsyblException("Error switch node not having exactly 2 adjacent nodes " + getId()); + } + return getAdjacentNodes().get(getAdjacentNodes().get(0).equals(adj) ? 1 : 0); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/TwtEdge.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/TwtEdge.java new file mode 100644 index 000000000..15a7fb0de --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/TwtEdge.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.model; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * + * @author Massimo Ferraro + */ +public class TwtEdge extends Edge { + + private List snakeLine = new ArrayList<>(); + + public TwtEdge(Node node1, Node node2) { + super(node1, node2); + } + + public List getSnakeLine() { + return snakeLine; + } + + public void setSnakeLine(List snakeLine) { + this.snakeLine = Objects.requireNonNull(snakeLine); + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/model/UndefinedBlock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/UndefinedBlock.java new file mode 100644 index 000000000..4a0d01b43 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/model/UndefinedBlock.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package com.powsybl.sld.model; + +import com.powsybl.sld.layout.LayoutParameters; + +import java.util.List; +import java.util.Objects; + +/** + * A block group that cannot be correctly decomposed anymore. + * All subBlocks are superposed. + * + * @author Geoffroy Jamgotchian + */ +public class UndefinedBlock extends AbstractComposedBlock { + + public UndefinedBlock(List subBlocks) { + super(Type.UNDEFINED, subBlocks); + this.subBlocks = Objects.requireNonNull(subBlocks); + } + + public UndefinedBlock(List subBlocks, Cell cell) { + this(subBlocks); + setCell(cell); + } + + @Override + public void sizing() { + for (Block block : subBlocks) { + block.sizing(); + } + if (getPosition().getOrientation() == Orientation.VERTICAL) { + // TODO + } else { + throw new UnsupportedOperationException("Horizontal layout of undefined block not supported"); + } + } + + @Override + public void coordVerticalCase(LayoutParameters layoutParam) { + for (Block block : subBlocks) { + block.setX(getCoord().getX()); + block.setY(getCoord().getY()); + block.setXSpan(getCoord().getXSpan()); + block.setYSpan(getCoord().getYSpan()); + block.coordVerticalCase(layoutParam); + } + } + + @Override + public void coordHorizontalCase(LayoutParameters layoutParam) { + throw new UnsupportedOperationException("Horizontal layout of undefined block not supported"); + } + + @Override + public String toString() { + return "UndefinedBlock(subBlocks=" + subBlocks + ")"; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/postprocessor/GraphBuildPostProcessor.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/postprocessor/GraphBuildPostProcessor.java new file mode 100644 index 000000000..ec3e18190 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/postprocessor/GraphBuildPostProcessor.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package com.powsybl.sld.postprocessor; + +import com.powsybl.sld.model.Graph; + +/** + * @author Franck Lecuyer + */ +public interface GraphBuildPostProcessor { + String getId(); + + void addNode(Graph graph); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/postprocessor/GraphBuildPostProcessorMock.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/postprocessor/GraphBuildPostProcessorMock.java new file mode 100644 index 000000000..594b50c0d --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/postprocessor/GraphBuildPostProcessorMock.java @@ -0,0 +1,31 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package com.powsybl.sld.postprocessor; + +import com.google.auto.service.AutoService; +import com.powsybl.sld.model.Graph; + +import java.util.Objects; + +/** + * @author Franck Lecuyer + */ +@AutoService(GraphBuildPostProcessor.class) +public class GraphBuildPostProcessorMock implements GraphBuildPostProcessor { + private static final String ID = "PostProcessorMock"; + + public String getId() { + return ID; + } + + public void addNode(Graph graph) { + Objects.requireNonNull(graph); + + // this mock does nothing + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultNodeLabelConfiguration.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultNodeLabelConfiguration.java new file mode 100644 index 000000000..43266bac2 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultNodeLabelConfiguration.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.model.BusCell; +import com.powsybl.sld.model.BusNode; +import com.powsybl.sld.model.ExternCell; +import com.powsybl.sld.model.FeederNode; +import com.powsybl.sld.model.Node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author Franck Lecuyer + */ +public class DefaultNodeLabelConfiguration implements NodeLabelConfiguration { + + private final ComponentLibrary componentLibrary; + + private static final double LABEL_OFFSET = 5d; + private static final int FONT_SIZE = 8; + + public DefaultNodeLabelConfiguration(ComponentLibrary componentLibrary) { + this.componentLibrary = componentLibrary; + } + + @Override + public List getLabelsPosition(Node node) { + Objects.requireNonNull(node); + + List res = new ArrayList<>(); + + if (node instanceof FeederNode) { + BusCell.Direction direction = node.getCell() != null + ? ((ExternCell) node.getCell()).getDirection() + : BusCell.Direction.UNDEFINED; + + double yShift = -LABEL_OFFSET; + String positionName = ""; + if (node.getCell() != null) { + yShift = direction == BusCell.Direction.TOP + ? -LABEL_OFFSET + : ((int) (componentLibrary.getSize(node.getComponentType()).getHeight()) + FONT_SIZE + LABEL_OFFSET); + positionName = direction == BusCell.Direction.TOP ? "N" : "S"; + } + + String nodeLabel = node.getId() + "_" + positionName + "_LABEL"; + res.add(new LabelPosition(nodeLabel, -LABEL_OFFSET, yShift)); + } else if (node instanceof BusNode) { + String nodeLabel = node.getId() + "_NW_LABEL"; + res.add(new LabelPosition(nodeLabel, -LABEL_OFFSET, -LABEL_OFFSET)); + } + + return res; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSVGWriter.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSVGWriter.java new file mode 100644 index 000000000..789a13278 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSVGWriter.java @@ -0,0 +1,1179 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.powsybl.commons.exceptions.UncheckedTransformerException; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.library.*; +import com.powsybl.sld.model.Node; +import com.powsybl.sld.model.*; +import com.powsybl.sld.svg.GraphMetadata.ArrowMetadata; +import com.powsybl.sld.svg.SubstationDiagramInitialValueProvider.Direction; +import org.apache.batik.anim.dom.SVGOMDocument; +import org.apache.batik.dom.GenericDOMImplementation; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.math3.util.Precision; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.*; +import org.w3c.dom.svg.SVGElement; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import static com.powsybl.sld.library.ComponentTypeName.*; +import static com.powsybl.sld.svg.SubstationDiagramStyles.*; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class DefaultSVGWriter implements SVGWriter { + + protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultSVGWriter.class); + + protected static final String CLASS = "class"; + protected static final String TRANSFORM = "transform"; + protected static final String TRANSLATE = "translate"; + protected static final String ROTATE = "rotate"; + protected static final int FONT_SIZE = 8; + protected static final String FONT_FAMILY = "Verdana"; + protected static final double LABEL_OFFSET = 5d; + protected static final int FONT_VOLTAGE_LEVEL_LABEL_SIZE = 12; + protected static final String POLYLINE = "polyline"; + protected static final String POINTS = "points"; + protected static final String STROKE = "stroke"; + protected static final String WINDING1 = "WINDING1"; + protected static final String WINDING2 = "WINDING2"; + protected static final String WINDING3 = "WINDING3"; + + protected final ComponentLibrary componentLibrary; + + protected final LayoutParameters layoutParameters; + + Function nodeDirection = node -> + (node instanceof FeederNode && node.getCell() != null) ? ((ExternCell) node.getCell()).getDirection() : BusCell.Direction.UNDEFINED; + + public DefaultSVGWriter(ComponentLibrary componentLibrary, LayoutParameters layoutParameters) { + this.componentLibrary = Objects.requireNonNull(componentLibrary); + this.layoutParameters = Objects.requireNonNull(layoutParameters); + } + + /** + * Create the SVGDocument corresponding to the graph + * + * @param graph graph + * @param svgFile file + */ + @Override + public GraphMetadata write(String prefixId, + Graph graph, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Path svgFile) { + try (Writer writer = Files.newBufferedWriter(svgFile)) { + return write(prefixId, graph, initProvider, styleProvider, nodeLabelConfiguration, writer); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Create the SVGDocument corresponding to the graph + * + * @param graph graph + * @param writer writer + */ + @Override + public GraphMetadata write(String prefixId, + Graph graph, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Writer writer) { + DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); + + Document document = domImpl.createDocument("http://www.w3.org/2000/svg", "svg", null); + Element style = document.createElement("style"); + + StringBuilder graphStyle = new StringBuilder(); + graphStyle.append(componentLibrary.getStyleSheet()); + + Set listUsedComponentSVG = new HashSet<>(); + + graph.getNodes().forEach(n -> { + Optional nodeStyle = styleProvider.getNodeStyle(n, layoutParameters.isAvoidSVGComponentsDuplication()); + nodeStyle.ifPresent(graphStyle::append); + listUsedComponentSVG.add(n.getComponentType()); + }); + graph.getEdges().forEach(e -> { + Optional wireStyle = styleProvider.getWireStyle(e); + wireStyle.ifPresent(graphStyle::append); + }); + CDATASection cd = document.createCDATASection(graphStyle.toString()); + style.appendChild(cd); + + document.adoptNode(style); + document.getDocumentElement().appendChild(style); + + createDefsSVGComponents(document, listUsedComponentSVG); + + GraphMetadata metadata = writegraph(prefixId, graph, document, initProvider, styleProvider, nodeLabelConfiguration); + + try { + DOMSource source = new DOMSource(document); + StreamResult result = new StreamResult(writer); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + transformer.transform(source, result); + } catch (TransformerException e) { + throw new UncheckedTransformerException(e); + } + + return metadata; + } + + /** + * Create the SVGDocument corresponding to the graph + */ + protected GraphMetadata writegraph(String prefixId, + Graph graph, + Document document, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration) { + GraphMetadata metadata = new GraphMetadata(); + + Element root = document.createElement("g"); + + if (layoutParameters.isShowGrid()) { + root.appendChild(drawGrid(prefixId, graph, document, metadata)); + } + + AnchorPointProvider anchorPointProvider = (type, id) -> { + if (type.equals(BUSBAR_SECTION)) { + BusNode busbarSectionNode = (BusNode) graph.getNode(id); + List result = new ArrayList<>(); + result.add(new AnchorPoint(0, 0, AnchorOrientation.HORIZONTAL)); + for (int i = 1; i < 2 * busbarSectionNode.getPosition().getHSpan(); i++) { + result.add(new AnchorPoint( + ((double) i / 2) * layoutParameters.getCellWidth() - layoutParameters.getHorizontalBusPadding() / 2, + 0, AnchorOrientation.VERTICAL)); + } + result.add(new AnchorPoint(busbarSectionNode.getPxWidth(), 0, AnchorOrientation.HORIZONTAL)); + return result; + } + return componentLibrary.getAnchorPoints(type); + }; + + if (layoutParameters.isShiftFeedersPosition()) { + shiftFeedersPosition(graph, layoutParameters.getScaleShiftFeedersPosition()); + } + + drawNodes(prefixId, root, graph, metadata, anchorPointProvider, initProvider, styleProvider, nodeLabelConfiguration); + drawEdges(prefixId, root, graph, metadata, anchorPointProvider, initProvider, styleProvider); + + // the drawing of the voltageLevel graph label is done at the end in order to + // facilitate the move of a voltageLevel in the diagram + drawGraphLabel(prefixId, root, graph, metadata); + + document.adoptNode(root); + document.getDocumentElement().appendChild(root); + + return metadata; + } + + /** + * Create the SVGDocument corresponding to the substation graph + * + * @param graph substation graph + * @param svgFile file + */ + @Override + public GraphMetadata write(String prefixId, + SubstationGraph graph, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Path svgFile) { + try (Writer writer = Files.newBufferedWriter(svgFile)) { + return write(prefixId, graph, initProvider, styleProvider, nodeLabelConfiguration, writer); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Create the SVGDocument corresponding to the substation graph + * + * @param graph substation graph + * @param writer writer + */ + @Override + public GraphMetadata write(String prefixId, + SubstationGraph graph, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Writer writer) { + DOMImplementation domImpl = GenericDOMImplementation.getDOMImplementation(); + + Document document = domImpl.createDocument("http://www.w3.org/2000/svg", "svg", null); + Element style = document.createElement("style"); + + Set listUsedComponentSVG = new HashSet<>(); + + StringBuilder graphStyle = new StringBuilder(); + graphStyle.append(componentLibrary.getStyleSheet()); + + for (Graph vlGraph : graph.getNodes()) { + vlGraph.getNodes().forEach(n -> { + Optional nodeStyle = styleProvider.getNodeStyle(n, layoutParameters.isAvoidSVGComponentsDuplication()); + nodeStyle.ifPresent(graphStyle::append); + listUsedComponentSVG.add(n.getComponentType()); + }); + vlGraph.getEdges().forEach(e -> { + Optional wireStyle = styleProvider.getWireStyle(e); + wireStyle.ifPresent(graphStyle::append); + }); + } + CDATASection cd = document.createCDATASection(graphStyle.toString()); + style.appendChild(cd); + + document.adoptNode(style); + document.getDocumentElement().appendChild(style); + + createDefsSVGComponents(document, listUsedComponentSVG); + + GraphMetadata metadata = writegraph(prefixId, graph, document, initProvider, styleProvider, nodeLabelConfiguration); + + try { + DOMSource source = new DOMSource(document); + StreamResult result = new StreamResult(writer); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + transformer.transform(source, result); + } catch (TransformerException e) { + throw new UncheckedTransformerException(e); + } + + return metadata; + } + + @Override + public LayoutParameters getLayoutParameters() { + return layoutParameters; + } + + @Override + public ComponentLibrary getComponentLibrary() { + return componentLibrary; + } + + /** + * Create the SVGDocument corresponding to the substation graph + */ + protected GraphMetadata writegraph(String prefixId, + SubstationGraph graph, + Document document, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration) { + GraphMetadata metadata = new GraphMetadata(); + + Element root = document.createElement("g"); + + // Drawing grid lines + if (layoutParameters.isShowGrid()) { + for (Graph vlGraph : graph.getNodes()) { + root.appendChild(drawGrid(prefixId, vlGraph, document, metadata)); + } + } + + // Drawing the voltageLevels + for (Graph vlGraph : graph.getNodes()) { + AnchorPointProvider anchorPointProvider = (type, id) -> { + if (type.equals(BUSBAR_SECTION)) { + BusNode busbarSectionNode = (BusNode) vlGraph.getNode(id); + List result = new ArrayList<>(); + result.add(new AnchorPoint(0, 0, AnchorOrientation.HORIZONTAL)); + for (int i = 1; i < 2 * busbarSectionNode.getPosition().getHSpan(); i++) { + result.add(new AnchorPoint( + ((double) i / 2) * layoutParameters.getCellWidth() - layoutParameters.getHorizontalBusPadding() / 2, + 0, AnchorOrientation.VERTICAL)); + } + result.add(new AnchorPoint(busbarSectionNode.getPxWidth(), 0, AnchorOrientation.HORIZONTAL)); + return result; + } else { + return componentLibrary.getAnchorPoints(type); + } + }; + + if (layoutParameters.isShiftFeedersPosition()) { + shiftFeedersPosition(vlGraph, layoutParameters.getScaleShiftFeedersPosition()); + } + + drawNodes(prefixId, root, vlGraph, metadata, anchorPointProvider, initProvider, styleProvider, nodeLabelConfiguration); + drawEdges(prefixId, root, vlGraph, metadata, anchorPointProvider, initProvider, styleProvider); + } + + drawSnakeLines(prefixId, root, graph, metadata); + + // the drawing of the voltageLevel graph labels is done at the end in order to + // facilitate the move of a voltageLevel in the diagram + for (Graph vlGraph : graph.getNodes()) { + drawGraphLabel(prefixId, root, vlGraph, metadata); + } + + Element style = document.createElement("style"); + + StringBuilder graphStyle = new StringBuilder(); + graphStyle.append(componentLibrary.getStyleSheet()); + + for (Graph vlGraph : graph.getNodes()) { + vlGraph.getNodes().forEach(n -> { + Optional nodeStyle = styleProvider.getNodeStyle(n, layoutParameters.isAvoidSVGComponentsDuplication()); + nodeStyle.ifPresent(graphStyle::append); + }); + vlGraph.getEdges().forEach(e -> { + Optional wireStyle = styleProvider.getWireStyle(e); + wireStyle.ifPresent(graphStyle::append); + }); + } + + String cssStr = graphStyle.toString() + .replace("\r\n", "\n") // workaround for https://bugs.openjdk.java.net/browse/JDK-8133452 + .replace("\r", "\n"); + CDATASection cd = document.createCDATASection(cssStr); + style.appendChild(cd); + + document.adoptNode(style); + document.getDocumentElement().appendChild(style); + + document.adoptNode(root); + document.getDocumentElement().appendChild(root); + + return metadata; + } + + /* + * Drawing the grid lines (if required) + */ + protected Element drawGrid(String prefixId, Graph graph, Document document, GraphMetadata metadata) { + int maxH = graph.getNodeBuses().stream() + .mapToInt(nodeBus -> nodeBus.getPosition().getH() + nodeBus.getPosition().getHSpan()) + .max().orElse(0); + int maxV = graph.getNodeBuses().stream() + .mapToInt(nodeBus -> nodeBus.getPosition().getV()) + .max().orElse(1) - 1; + + Element gridRoot = document.createElement("g"); + String gridId = prefixId + "GRID_" + graph.getVoltageLevel().getId(); + gridRoot.setAttribute("id", gridId); + gridRoot.setAttribute(CLASS, SubstationDiagramStyles.GRID_STYLE_CLASS); + gridRoot.setAttribute(TRANSFORM, + TRANSLATE + "(" + layoutParameters.getTranslateX() + "," + layoutParameters.getTranslateY() + ")"); + // vertical lines + for (int i = 0; i < maxH + 1; i++) { + gridRoot.appendChild(drawGridVerticalLine(document, graph, maxV, + graph.getX() + layoutParameters.getInitialXBus() + i * layoutParameters.getCellWidth())); + } + + // StackHeight Horizontal lines + gridRoot.appendChild(drawGridHorizontalLine(document, graph, maxH, + graph.getY() + layoutParameters.getInitialYBus() - layoutParameters.getStackHeight())); + gridRoot.appendChild(drawGridHorizontalLine(document, graph, maxH, + graph.getY() + layoutParameters.getInitialYBus() + layoutParameters.getStackHeight() + + layoutParameters.getVerticalSpaceBus() * maxV)); + + // internCellHeight Horizontal lines + gridRoot.appendChild(drawGridHorizontalLine(document, graph, maxH, + graph.getY() + layoutParameters.getInitialYBus() - layoutParameters.getInternCellHeight())); + gridRoot.appendChild(drawGridHorizontalLine(document, graph, maxH, + graph.getY() + layoutParameters.getInitialYBus() + layoutParameters.getInternCellHeight() + + layoutParameters.getVerticalSpaceBus() * maxV)); + + metadata.addNodeMetadata(new GraphMetadata.NodeMetadata(gridId, + graph.getVoltageLevel().getId(), + null, + null, + null, + false, + BusCell.Direction.UNDEFINED, + false)); + + return gridRoot; + } + + protected Element drawGridHorizontalLine(Document document, Graph graph, int maxH, double y) { + return drawGridLine(document, + layoutParameters.getInitialXBus() + graph.getX(), y, + layoutParameters.getInitialXBus() + maxH * layoutParameters.getCellWidth() + graph.getX(), y); + } + + protected Element drawGridVerticalLine(Document document, Graph graph, int maxV, double x) { + return drawGridLine(document, + x, layoutParameters.getInitialYBus() + - layoutParameters.getStackHeight() - layoutParameters.getExternCellHeight() + graph.getY(), + x, layoutParameters.getInitialYBus() + + layoutParameters.getStackHeight() + layoutParameters.getExternCellHeight() + + layoutParameters.getVerticalSpaceBus() * maxV + graph.getY()); + } + + protected Element drawGridLine(Document document, double x1, double y1, double x2, double y2) { + Element line = document.createElement("line"); + line.setAttribute("x1", Double.toString(x1)); + line.setAttribute("x2", Double.toString(x2)); + line.setAttribute("y1", Double.toString(y1)); + line.setAttribute("y2", Double.toString(y2)); + return line; + } + + + /* + * Drawing the voltageLevel graph nodes + */ + protected void drawNodes(String prefixId, Element root, Graph graph, GraphMetadata metadata, + AnchorPointProvider anchorPointProvider, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration) { + graph.getNodes().forEach(node -> { + try { + String nodeId = SubstationDiagramStyles.escapeId(URLEncoder.encode(prefixId + node.getId(), StandardCharsets.UTF_8.name())); + Element g = root.getOwnerDocument().createElement("g"); + g.setAttribute("id", nodeId); + + g.setAttribute(CLASS, node.getComponentType() + " " + SubstationDiagramStyles.escapeId(nodeId)); + + if (node.getType() == Node.NodeType.BUS) { + drawBus((BusNode) node, g); + } else { + incorporateComponents(prefixId, node, g, styleProvider); + } + + BusCell.Direction direction = (node instanceof FeederNode && node.getCell() != null) ? ((ExternCell) node.getCell()).getDirection() : BusCell.Direction.UNDEFINED; + + if (!node.isFictitious()) { + drawNodeLabel(prefixId, g, node, initProvider, nodeLabelConfiguration); + } + root.appendChild(g); + + setMetadata(metadata, node, nodeId, graph, direction, anchorPointProvider); + + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + }); + } + + protected void setMetadata(GraphMetadata metadata, Node node, String nodeId, Graph graph, BusCell.Direction direction, AnchorPointProvider anchorPointProvider) { + String nextVId = null; + if (node instanceof FeederBranchNode) { + nextVId = ((FeederBranchNode) node).getVlOtherSide().getId(); + } + + metadata.addNodeMetadata( + new GraphMetadata.NodeMetadata(nodeId, graph.getVoltageLevel().getId(), nextVId, + node.getComponentType(), node.getRotationAngle(), + node.isOpen(), direction, false)); + if (node.getType() == Node.NodeType.BUS) { + metadata.addComponentMetadata(new ComponentMetadata(BUSBAR_SECTION, + nodeId, + anchorPointProvider.getAnchorPoints(BUSBAR_SECTION, node.getId()), + new ComponentSize(0, 0))); + } else { + if (metadata.getComponentMetadata(node.getComponentType()) == null) { + metadata.addComponentMetadata(new ComponentMetadata(node.getComponentType(), + null, + componentLibrary.getAnchorPoints(node.getComponentType()), + componentLibrary.getSize(node.getComponentType()))); + } + } + } + + protected void drawNodeLabel(String prefixId, Element g, Node node, + SubstationDiagramInitialValueProvider initProvider, + NodeLabelConfiguration nodeLabelConfiguration) { + List labelsNode = initProvider.getNodeLabelValue(node); + List labelsPosition = nodeLabelConfiguration.getLabelsPosition(node); + + if (labelsPosition.size() != labelsNode.size()) { + throw new AssertionError("Number of node labels <> Number of labels positions"); + } + + for (int i = 0; i < labelsNode.size(); ++i) { + drawLabel(prefixId + labelsPosition.get(i).getPositionName(), labelsNode.get(i), node.isRotated(), + labelsPosition.get(i).getdX(), labelsPosition.get(i).getdY(), + g, FONT_SIZE); + } + } + + /* + * Drawing the graph label + */ + protected void drawGraphLabel(String prefixId, Element root, Graph graph, GraphMetadata metadata) { + // drawing the label of the voltageLevel + String idLabelVoltageLevel = prefixId + "LABEL_VL_" + graph.getVoltageLevel().getId(); + Element gLabel = root.getOwnerDocument().createElement("g"); + gLabel.setAttribute("id", idLabelVoltageLevel); + + drawLabel(null, graph.isUseName() + ? graph.getVoltageLevel().getName() + : graph.getVoltageLevel().getId(), + false, graph.getX(), graph.getY(), gLabel, FONT_VOLTAGE_LEVEL_LABEL_SIZE); + root.appendChild(gLabel); + + metadata.addNodeMetadata(new GraphMetadata.NodeMetadata(idLabelVoltageLevel, + graph.getVoltageLevel().getId(), + null, + null, + null, + false, + BusCell.Direction.UNDEFINED, + true)); + } + + /* + * Drawing the voltageLevel graph busbar sections + */ + protected void drawBus(BusNode node, Element g) { + Element line = g.getOwnerDocument().createElement("line"); + line.setAttribute("x1", "0"); + line.setAttribute("y1", "0"); + if (node.isRotated()) { + line.setAttribute("x2", "0"); + line.setAttribute("y2", String.valueOf(node.getPxWidth())); + } else { + line.setAttribute("x2", String.valueOf(node.getPxWidth())); + line.setAttribute("y2", "0"); + } + + g.appendChild(line); + + g.setAttribute(TRANSFORM, TRANSLATE + "(" + (layoutParameters.getTranslateX() + node.getX()) + "," + + (layoutParameters.getTranslateY() + node.getY()) + ")"); + } + + /* + * Drawing the voltageLevel graph busbar section names and feeder names + */ + protected void drawLabel(String idLabel, String str, boolean rotated, double xShift, double yShift, Element g, + int fontSize) { + Element label = g.getOwnerDocument().createElement("text"); + if (!StringUtils.isEmpty(idLabel)) { + label.setAttribute("id", idLabel); + } + label.setAttribute("x", String.valueOf(xShift)); + label.setAttribute("y", String.valueOf(yShift)); + label.setAttribute("font-family", FONT_FAMILY); + label.setAttribute("font-size", Integer.toString(fontSize)); + label.setAttribute(CLASS, SubstationDiagramStyles.LABEL_STYLE_CLASS); + Text text = g.getOwnerDocument().createTextNode(str); + label.setAttribute(TRANSFORM, ROTATE + "(" + (rotated ? -90 : 0) + "," + 0 + "," + 0 + ")"); + label.appendChild(text); + g.appendChild(label); + } + + protected boolean canInsertComponentSVG(Node node) { + return layoutParameters.isShowInternalNodes() || + ((!node.isFictitious() && node.getType() != Node.NodeType.SHUNT) || + (node.isFictitious() && node.getComponentType().equals(THREE_WINDINGS_TRANSFORMER))); + } + + protected void incorporateComponents(String prefixId, Node node, Element g, SubstationDiagramStyleProvider styleProvider) { + SVGOMDocument obj = componentLibrary.getSvgDocument(node.getComponentType()); + transformComponent(node, g); + if (obj != null && canInsertComponentSVG(node)) { + String componentDefsId = node.getComponentType(); + if (node.getComponentType().equals(BREAKER) + || node.getComponentType().equals(DISCONNECTOR)) { + componentDefsId += node.isOpen() ? "-open" : "-closed"; + } + insertComponentSVGIntoDocumentSVG(prefixId, obj, g, node, styleProvider, componentLibrary.getSize(node.getComponentType()), componentDefsId); + } + } + + protected Map getAttributesTransformer(Node node, + String idWinding, + SubstationDiagramStyleProvider styleProvider, + ComponentSize size) { + Map attributes = new HashMap<>(); + Optional color = Optional.empty(); + VoltageLevel vl = node.getGraph().getVoltageLevel(); + + // We will rotate the 3WT SVG, if cell orientation is BOTTOM + boolean rotateSVG = node instanceof Fictitious3WTNode + && node.getCell() != null + && ((ExternCell) node.getCell()).getDirection() == BusCell.Direction.BOTTOM; + + if (idWinding.endsWith(WINDING1)) { + if (node instanceof Fictitious3WTNode) { + ThreeWindingsTransformer.Side otherSide = ThreeWindingsTransformer.Side.ONE; + + if (((Fictitious3WTNode) node).getTransformer().getLeg1().getTerminal().getVoltageLevel() == vl) { + otherSide = !rotateSVG ? ThreeWindingsTransformer.Side.TWO : ThreeWindingsTransformer.Side.THREE; + } else if (((Fictitious3WTNode) node).getTransformer().getLeg2().getTerminal().getVoltageLevel() == vl) { + otherSide = !rotateSVG ? ThreeWindingsTransformer.Side.ONE : ThreeWindingsTransformer.Side.THREE; + } else if (((Fictitious3WTNode) node).getTransformer().getLeg3().getTerminal().getVoltageLevel() == vl) { + otherSide = !rotateSVG ? ThreeWindingsTransformer.Side.ONE : ThreeWindingsTransformer.Side.TWO; + } + color = styleProvider.getNode3WTStyle((Fictitious3WTNode) node, otherSide); + } else { + color = styleProvider.getNode2WTStyle((Feeder2WTNode) node, TwoWindingsTransformer.Side.ONE); + } + } else if (idWinding.endsWith(WINDING2)) { // second winding + if (node instanceof Fictitious3WTNode) { + ThreeWindingsTransformer.Side otherSide = ThreeWindingsTransformer.Side.ONE; + + if (((Fictitious3WTNode) node).getTransformer().getLeg1().getTerminal().getVoltageLevel() == vl) { + otherSide = !rotateSVG ? ThreeWindingsTransformer.Side.THREE : ThreeWindingsTransformer.Side.TWO; + } else if (((Fictitious3WTNode) node).getTransformer().getLeg2().getTerminal().getVoltageLevel() == vl) { + otherSide = !rotateSVG ? ThreeWindingsTransformer.Side.THREE : ThreeWindingsTransformer.Side.ONE; + } else if (((Fictitious3WTNode) node).getTransformer().getLeg3().getTerminal().getVoltageLevel() == vl) { + otherSide = !rotateSVG ? ThreeWindingsTransformer.Side.TWO : ThreeWindingsTransformer.Side.ONE; + } + color = styleProvider.getNode3WTStyle((Fictitious3WTNode) node, otherSide); + } else { + color = styleProvider.getNode2WTStyle((Feeder2WTNode) node, TwoWindingsTransformer.Side.TWO); + } + } else if (idWinding.endsWith(WINDING3) && node instanceof Fictitious3WTNode) { // third winding + if (((Fictitious3WTNode) node).getTransformer().getLeg1().getTerminal().getVoltageLevel() == vl) { + color = styleProvider.getNode3WTStyle((Fictitious3WTNode) node, ThreeWindingsTransformer.Side.ONE); + } else if (((Fictitious3WTNode) node).getTransformer().getLeg2().getTerminal().getVoltageLevel() == vl) { + color = styleProvider.getNode3WTStyle((Fictitious3WTNode) node, ThreeWindingsTransformer.Side.TWO); + } else if (((Fictitious3WTNode) node).getTransformer().getLeg3().getTerminal().getVoltageLevel() == vl) { + color = styleProvider.getNode3WTStyle((Fictitious3WTNode) node, ThreeWindingsTransformer.Side.THREE); + } + } + if (color.isPresent()) { + attributes.put(STROKE, color.get()); + } + if (rotateSVG) { // SVG element rotation + attributes.put(TRANSFORM, ROTATE + "(" + 180 + "," + size.getWidth() / 2 + "," + size.getHeight() / 2 + ")"); + // We store the rotation angle in order to transform correctly the anchor points when further drawing the edges + node.setRotationAngle(180.); + } + return attributes; + } + + protected Map getAttributesInductor(Node node, SubstationDiagramStyleProvider styleProvider) { + Map attributes = new HashMap<>(); + Optional color = styleProvider.getColor(((Feeder2WTNode) node).getVlOtherSide()); + if (color.isPresent()) { + attributes.put(STROKE, color.get()); + } + return attributes; + } + + /* + * Handling the transformer SVG part (rotation, colorization) + */ + protected void handleTransformerSvgDocument(Node node, SubstationDiagramStyleProvider styleProvider, + ComponentSize size, org.w3c.dom.Node n) { + if (((SVGElement) n).getId().endsWith(WINDING1)) { // first winding + getAttributesTransformer(node, WINDING1, styleProvider, size).entrySet().stream().forEach(e -> { + ((Element) n).removeAttribute(e.getKey()); + ((Element) n).setAttribute(e.getKey(), e.getValue()); + }); + } else if (((SVGElement) n).getId().endsWith(WINDING2)) { // second winding + getAttributesTransformer(node, WINDING2, styleProvider, size).entrySet().stream().forEach(e -> { + ((Element) n).removeAttribute(e.getKey()); + ((Element) n).setAttribute(e.getKey(), e.getValue()); + }); + } else if (((SVGElement) n).getId().endsWith(WINDING3) && node instanceof Fictitious3WTNode) { // third winding + getAttributesTransformer(node, WINDING3, styleProvider, size).entrySet().stream().forEach(e -> { + ((Element) n).removeAttribute(e.getKey()); + ((Element) n).setAttribute(e.getKey(), e.getValue()); + }); + } + } + + /* + * Handling the inductor SVG part (colorization) + */ + protected void handleInductorSvgDocument(Node node, SubstationDiagramStyleProvider styleProvider, org.w3c.dom.Node n) { + getAttributesInductor(node, styleProvider).entrySet().stream().forEach(e -> { + ((Element) n).removeAttribute(e.getKey()); + ((Element) n).setAttribute(e.getKey(), e.getValue()); + }); + } + + protected void insertComponentSVGIntoDocumentSVG(String prefixId, + SVGOMDocument obj, Element g, Node node, + SubstationDiagramStyleProvider styleProvider, + ComponentSize size, + String componentDefsId) { + if (!layoutParameters.isAvoidSVGComponentsDuplication()) { + // The following code work correctly considering SVG part describing the component is the first child of "obj" the SVGDocument. + // If SVG are written otherwise, it will not work correctly. + for (int i = 0; i < obj.getChildNodes().item(0).getChildNodes().getLength(); i++) { + org.w3c.dom.Node n = obj.getChildNodes().item(0).getChildNodes().item(i).cloneNode(true); + + if (n instanceof SVGElement) { + if (node instanceof Fictitious3WTNode || + (node instanceof Feeder2WTNode && node.getComponentType().equals(TWO_WINDINGS_TRANSFORMER))) { + handleTransformerSvgDocument(node, styleProvider, size, n); + } else if (node instanceof Feeder2WTNode && node.getComponentType().equals(INDUCTOR)) { + handleInductorSvgDocument(node, styleProvider, n); + } + } + + // Adding prefixId and node id before id of n : to ensure unicity of ids + if (n.getAttributes() != null) { + org.w3c.dom.Node nodeId = n.getAttributes().getNamedItem("id"); + if (nodeId != null) { + String nodeIdValue = nodeId.getTextContent(); + String gIdValue = StringUtils.removeStart(g.getAttribute("id"), prefixId); + if (StringUtils.isEmpty(prefixId)) { + nodeId.setTextContent(gIdValue + "_" + nodeIdValue); + } else { + nodeId.setTextContent(prefixId + gIdValue + "_" + nodeIdValue); + } + } + } + + g.getOwnerDocument().adoptNode(n); + g.appendChild(n); + } + } else { + // Adding markup to reuse the svg defined in the part + if (node instanceof Fictitious3WTNode || + (node instanceof Feeder2WTNode && node.getComponentType().equals(TWO_WINDINGS_TRANSFORMER))) { + + Element eltUse1 = g.getOwnerDocument().createElement("use"); + eltUse1.setAttribute("href", "#" + componentDefsId + "-WINDING1"); + getAttributesTransformer(node, WINDING1, styleProvider, size).entrySet().stream().forEach(e -> eltUse1.setAttribute(e.getKey(), e.getValue())); + g.getOwnerDocument().adoptNode(eltUse1); + g.appendChild(eltUse1); + + Element eltUse2 = g.getOwnerDocument().createElement("use"); + eltUse2.setAttribute("href", "#" + componentDefsId + "-WINDING2"); + getAttributesTransformer(node, WINDING2, styleProvider, size).entrySet().stream().forEach(e -> eltUse2.setAttribute(e.getKey(), e.getValue())); + g.getOwnerDocument().adoptNode(eltUse2); + g.appendChild(eltUse2); + + if (node instanceof Fictitious3WTNode) { + Element eltUse3 = g.getOwnerDocument().createElement("use"); + eltUse3.setAttribute("href", "#" + componentDefsId + "-WINDING3"); + getAttributesTransformer(node, WINDING3, styleProvider, size).entrySet().stream().forEach(e -> eltUse3.setAttribute(e.getKey(), e.getValue())); + g.getOwnerDocument().adoptNode(eltUse3); + g.appendChild(eltUse3); + } + } else { + Element eltUse = g.getOwnerDocument().createElement("use"); + eltUse.setAttribute("href", "#" + componentDefsId); + + if (node instanceof Feeder2WTNode && node.getComponentType().equals(INDUCTOR)) { + getAttributesInductor(node, styleProvider).entrySet().stream().forEach(e -> eltUse.setAttribute(e.getKey(), e.getValue())); + } + + g.getOwnerDocument().adoptNode(eltUse); + g.appendChild(eltUse); + } + } + } + + protected void insertRotatedComponentSVGIntoDocumentSVG(String prefixId, + SVGOMDocument obj, Element g, double angle, + double cx, double cy, + String componentDefsId) { + if (!layoutParameters.isAvoidSVGComponentsDuplication()) { + // The following code work correctly considering SVG part describing the component is the first child of "obj" the SVGDocument. + // If SVG are written otherwise, it will not work correctly. + for (int i = 0; i < obj.getChildNodes().item(0).getChildNodes().getLength(); i++) { + org.w3c.dom.Node n = obj.getChildNodes().item(0).getChildNodes().item(i).cloneNode(true); + if (n.getNodeName().equals("g") && n.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) { + Element e = (Element) n; + e.setAttribute(TRANSFORM, ROTATE + "(" + angle + "," + cx + "," + cy + ")"); + } + + // Adding prefixId and g id before id of n : to ensure unicity of ids + if (n.getAttributes() != null) { + org.w3c.dom.Node nodeId = n.getAttributes().getNamedItem("id"); + if (nodeId != null) { + String nodeIdValue = nodeId.getTextContent(); + String gIdValue = StringUtils.removeStart(g.getAttribute("id"), prefixId); + if (StringUtils.isEmpty(prefixId)) { + nodeId.setTextContent(gIdValue + "_" + nodeIdValue); + } else { + nodeId.setTextContent(prefixId + gIdValue + "_" + nodeIdValue); + } + } + } + + g.getOwnerDocument().adoptNode(n); + g.appendChild(n); + } + } else { + // Adding markup to reuse the svg defined in the part + Element eltUse = g.getOwnerDocument().createElement("use"); + eltUse.setAttribute("href", "#" + componentDefsId); + eltUse.setAttribute(TRANSFORM, ROTATE + "(" + angle + "," + cx + "," + cy + ")"); + + g.getOwnerDocument().adoptNode(eltUse); + g.appendChild(eltUse); + } + } + + protected void transformComponent(Node node, Element g) { + ComponentSize componentSize = componentLibrary.getSize(node.getComponentType()); + + if (!node.isRotated()) { + g.setAttribute(TRANSFORM, + TRANSLATE + "(" + (layoutParameters.getTranslateX() + node.getX() - componentSize.getWidth() / 2) + "," + + (layoutParameters.getTranslateY() + node.getY() - componentSize.getHeight() / 2) + ")"); + return; + } + +/* + afester javafx library does not handle more than one transformation, yet, so + combine the couple of transformations, translation+rotation, in a single matrix transformation +*/ + int precision = 4; + + double angle = Math.toRadians(node.isRotated() ? 90 : 0); + double cosRo = Math.cos(angle); + double sinRo = Math.sin(angle); + double cdx = componentSize.getWidth() / 2; + double cdy = componentSize.getHeight() / 2; + + double e1 = layoutParameters.getTranslateX() - cdx * cosRo + cdy * sinRo + node.getX(); + double f1 = layoutParameters.getTranslateY() - cdx * sinRo - cdy * cosRo + node.getY(); + + g.setAttribute(TRANSFORM, + "matrix(" + Precision.round(cosRo, precision) + "," + Precision.round(sinRo, precision) + + "," + Precision.round(-sinRo, precision) + "," + Precision.round(cosRo, + precision) + "," + + Precision.round(e1, precision) + "," + Precision.round(f1, precision) + ")"); + } + + protected void transformArrow(List points, ComponentSize componentSize, double shift, Element g) { + + double x1 = points.get(0); + double y1 = points.get(1); + double x2 = points.get(2); + double y2 = points.get(3); + + if (points.size() > 4 && Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)) < 3 * componentSize.getHeight()) { + double x3 = points.get(4); + double y3 = points.get(5); + if (Math.sqrt((x3 - x2) * (x3 - x2) + (y3 - y2) * (y3 - y2)) > 3 * componentSize.getHeight()) { + x1 = x2; + y1 = y2; + x2 = x3; + y2 = y3; + } + } + double dx = x2 - x1; + double dy = y2 - y1; + + double angle = Math.atan(dx / dy); + if (!Double.isNaN(angle)) { + double cosRo = Math.cos(angle); + double sinRo = Math.sin(angle); + double cdx = componentSize.getWidth() / 2; + double cdy = componentSize.getHeight() / 2; + + double dist = this.layoutParameters.getArrowDistance(); + + double x = x1 + sinRo * (dist + shift); + double y = y1 + cosRo * (y1 > y2 ? -(dist + shift) : (dist + shift)); + + double e1 = layoutParameters.getTranslateX() - cdx * cosRo + cdy * sinRo + x; + double f1 = layoutParameters.getTranslateY() - cdx * sinRo - cdy * cosRo + y; + + int precision = 4; + g.setAttribute(TRANSFORM, + "matrix(" + Precision.round(cosRo, precision) + "," + Precision.round(sinRo, precision) + + "," + Precision.round(-sinRo, precision) + "," + Precision.round(cosRo, + precision) + "," + + Precision.round(e1, precision) + "," + Precision.round(f1, precision) + ")"); + } + } + + protected void insertArrowsAndLabels(String prefixId, String wireId, List points, Element root, Node n, GraphMetadata metadata, SubstationDiagramInitialValueProvider initProvider, SubstationDiagramStyleProvider styleProvider) { + InitialValue init = initProvider.getInitialValue(n); + ComponentMetadata cd = metadata.getComponentMetadata(ARROW); + + double shX = cd.getSize().getWidth() + LABEL_OFFSET; + double shY = cd.getSize().getHeight() - LABEL_OFFSET + (double) FONT_SIZE / 2; + + Element g1 = root.getOwnerDocument().createElement("g"); + String arrow1WireId = wireId + "_ARROW1"; + g1.setAttribute("id", arrow1WireId); + SVGOMDocument arr = componentLibrary.getSvgDocument(ARROW); + transformArrow(points, cd.getSize(), 0, g1); + double y1 = points.get(1); + double y2 = points.get(3); + String defsId = ARROW; + + Optional dir1 = init.getArrowDirection1(); + if (dir1.isPresent()) { + defsId += dir1.get() == Direction.UP ? "-arrow-up" : "-arrow-down"; + } + + if (y1 > y2) { + insertRotatedComponentSVGIntoDocumentSVG(prefixId, arr, g1, 180, cd.getSize().getWidth() / 2, cd.getSize().getHeight() / 2, defsId); + } else { + insertComponentSVGIntoDocumentSVG(prefixId, arr, g1, n, styleProvider, componentLibrary.getSize(n.getComponentType()), defsId); + } + Optional label1 = init.getLabel1(); + if (label1.isPresent()) { + drawLabel(null, label1.get(), false, shX, shY, g1, FONT_SIZE); + } + if (dir1.isPresent()) { + try { + g1.setAttribute(CLASS, "ARROW1_" + escapeId(URLEncoder.encode(n.getId(), StandardCharsets.UTF_8.name())) + "_" + dir1.get()); + if (layoutParameters.isAvoidSVGComponentsDuplication()) { + styleProvider.getAttributesArrow(1).entrySet().stream().forEach(e -> ((Element) g1.getFirstChild()).setAttribute(e.getKey(), e.getValue())); + } + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + } + root.appendChild(g1); + metadata.addArrowMetadata(new ArrowMetadata(arrow1WireId, wireId, layoutParameters.getArrowDistance())); + + Element g2 = root.getOwnerDocument().createElement("g"); + String arrow2WireId = wireId + "_ARROW2"; + g2.setAttribute("id", arrow2WireId); + transformArrow(points, cd.getSize(), 2 * cd.getSize().getHeight(), g2); + + defsId = ARROW; + Optional dir2 = init.getArrowDirection2(); + if (dir2.isPresent()) { + defsId += dir2.get() == Direction.UP ? "-arrow-up" : "-arrow-down"; + } + + if (y1 > y2) { + insertRotatedComponentSVGIntoDocumentSVG(prefixId, arr, g2, 180, 5, 5, defsId); + } else { + insertComponentSVGIntoDocumentSVG(prefixId, arr, g2, n, styleProvider, componentLibrary.getSize(n.getComponentType()), defsId); + } + Optional label2 = init.getLabel2(); + if (label2.isPresent()) { + drawLabel(null, label2.get(), false, shX, shY, g2, FONT_SIZE); + } + if (dir2.isPresent()) { + try { + g2.setAttribute(CLASS, "ARROW2_" + escapeClassName(URLEncoder.encode(n.getId(), StandardCharsets.UTF_8.name())) + "_" + dir2.get()); + if (layoutParameters.isAvoidSVGComponentsDuplication()) { + styleProvider.getAttributesArrow(2).entrySet().stream().forEach(e -> ((Element) g2.getFirstChild()).setAttribute(e.getKey(), e.getValue())); + } + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + } + Optional label3 = init.getLabel3(); + if (label3.isPresent()) { + drawLabel(null, label3.get(), false, -(label3.get().length() * (double) FONT_SIZE / 2 + LABEL_OFFSET), shY, g1, FONT_SIZE); + } + Optional label4 = init.getLabel4(); + if (label4.isPresent()) { + drawLabel(null, label4.get(), false, -(label4.get().length() * (double) FONT_SIZE / 2 + LABEL_OFFSET), shY, g2, FONT_SIZE); + } + + root.appendChild(g2); + metadata.addArrowMetadata(new ArrowMetadata(arrow2WireId, wireId, layoutParameters.getArrowDistance())); + } + + /* + * Drawing the voltageLevel graph edges + */ + protected void drawEdges(String prefixId, Element root, Graph graph, GraphMetadata metadata, AnchorPointProvider anchorPointProvider, SubstationDiagramInitialValueProvider initProvider, SubstationDiagramStyleProvider styleProvider) { + String vId = graph.getVoltageLevel().getId(); + try { + for (Edge edge : graph.getEdges()) { + // for unicity purpose (in substation diagram), we prefix the id of the WireMetadata with the voltageLevel id and "_" + String wireId = escapeId(URLEncoder.encode(prefixId + vId + "_Wire" + graph.getEdges().indexOf(edge), StandardCharsets.UTF_8.name())); + + Element g = root.getOwnerDocument().createElement(POLYLINE); + g.setAttribute("id", wireId); + + WireConnection anchorPoints = WireConnection.searchBetterAnchorPoints(anchorPointProvider, edge.getNode1(), edge.getNode2()); + + // Determine points of the polyline + List pol = anchorPoints.calculatePolylinePoints(edge.getNode1(), edge.getNode2(), + layoutParameters.isDrawStraightWires()); + + g.setAttribute(POINTS, pointsListToString(pol)); + g.setAttribute(CLASS, WIRE_STYLE_CLASS + " " + styleProvider.getIdWireStyle(edge)); + root.appendChild(g); + + metadata.addWireMetadata(new GraphMetadata.WireMetadata(wireId, + escapeClassName(URLEncoder.encode(edge.getNode1().getId(), StandardCharsets.UTF_8.name())), + escapeClassName(URLEncoder.encode(edge.getNode2().getId(), StandardCharsets.UTF_8.name())), + layoutParameters.isDrawStraightWires(), + false)); + + if (metadata.getComponentMetadata(ARROW) == null) { + metadata.addComponentMetadata(new ComponentMetadata(ARROW, + null, + componentLibrary.getAnchorPoints(ARROW), + componentLibrary.getSize(ARROW))); + } + + if (edge.getNode1() instanceof FeederNode) { + if (!(edge.getNode2() instanceof FeederNode)) { + insertArrowsAndLabels(prefixId, wireId, pol, root, edge.getNode1(), metadata, initProvider, styleProvider); + } + } else if (edge.getNode2() instanceof FeederNode) { + insertArrowsAndLabels(prefixId, wireId, pol, root, edge.getNode2(), metadata, initProvider, styleProvider); + } + } + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + } + + /* + * Drawing the substation graph edges (snakelines between voltageLevel diagram) + */ + protected void drawSnakeLines(String prefixId, Element root, SubstationGraph graph, GraphMetadata metadata) { + + for (TwtEdge edge : graph.getEdges()) { + String vId1 = edge.getNode1().getGraph().getVoltageLevel().getId(); + String vId2 = edge.getNode2().getGraph().getVoltageLevel().getId(); + try { + String wireId = escapeId(URLEncoder.encode(prefixId + vId1 + "_" + vId2 + "_" + "Wire" + graph.getEdges().indexOf(edge), StandardCharsets.UTF_8.name())); + Element g = root.getOwnerDocument().createElement(POLYLINE); + g.setAttribute("id", wireId); + + // Get points of the snakeLine + List pol = edge.getSnakeLine(); + + g.setAttribute(POINTS, pointsListToString(pol)); + + String vId; + if (edge.getNode1().getGraph().getVoltageLevel().getNominalV() > edge.getNode2().getGraph().getVoltageLevel().getNominalV()) { + vId = vId1; + } else { + vId = vId2; + } + + g.setAttribute(CLASS, SubstationDiagramStyles.WIRE_STYLE_CLASS + " " + SubstationDiagramStyles.WIRE_STYLE_CLASS + "_" + escapeClassName(vId)); + root.appendChild(g); + + metadata.addWireMetadata(new GraphMetadata.WireMetadata(wireId, + escapeClassName(URLEncoder.encode(edge.getNode1().getId(), StandardCharsets.UTF_8.name())), + escapeClassName(URLEncoder.encode(edge.getNode2().getId(), StandardCharsets.UTF_8.name())), + layoutParameters.isDrawStraightWires(), + true)); + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + + if (metadata.getComponentMetadata(ARROW) == null) { + metadata.addComponentMetadata(new ComponentMetadata(ARROW, + null, + componentLibrary.getAnchorPoints(ARROW), + componentLibrary.getSize(ARROW))); + } + } + } + + protected String pointsListToString(List pol) { + return IntStream.range(0, pol.size()) + .mapToObj(n -> n % 2 == 0 ? pol.get(n) + layoutParameters.getTranslateX() : pol.get(n) + layoutParameters.getTranslateY()) + .map(Object::toString) + .collect(Collectors.joining(",")); + } + + /** + * Adjust feeders height, positioning them on a descending/ascending ramp + * (depending on their BusCell direction) + */ + private void shiftFeedersPosition(Graph graph, double scaleShiftFeederNames) { + Map> orderedFeederNodesByDirection = graph.getNodes().stream() + .filter(node -> !node.isFictitious() && node instanceof FeederNode && node.getCell() != null) + .sorted(Comparator.comparing(Node::getX)) + .collect(Collectors.groupingBy(node -> nodeDirection.apply(node))); + + Map mapLev = Arrays.stream(BusCell.Direction.values()).collect(Collectors.toMap(d -> d, d -> 0.0)); + + Stream.of(BusCell.Direction.values()) + .filter(direction -> orderedFeederNodesByDirection.get(direction) != null) + .forEach(direction -> { + orderedFeederNodesByDirection.get(direction).stream().skip(1).forEach(node -> { + int componentHeight = (int) (componentLibrary.getSize(node.getComponentType()).getHeight()); + double oldY = node.getY() - graph.getY(); + double newY = mapLev.get(direction) + scaleShiftFeederNames * FONT_SIZE + (componentHeight == 0 ? LABEL_OFFSET : componentHeight); + node.setY(oldY + ((direction == BusCell.Direction.TOP) ? 1 : -1) * newY); + mapLev.put(direction, newY); + }); + }); + } + + /** + * Creation of the defs area for the SVG components + */ + protected void createDefsSVGComponents(Document document, Set listUsedComponentSVG) { + if (layoutParameters.isAvoidSVGComponentsDuplication()) { + Element defs = document.createElement("defs"); + + listUsedComponentSVG.stream().forEach(c -> { + SVGOMDocument obj = componentLibrary.getSvgDocument(c); + if (obj != null) { + Element group = document.createElement("g"); + group.setAttribute("id", c); + + insertSVGComponentIntoDefsArea(group, obj); + + defs.getOwnerDocument().adoptNode(group); + defs.appendChild(group); + } + }); + + // Adding also arrows + Element group = document.createElement("g"); + group.setAttribute("id", ARROW); + SVGOMDocument obj = componentLibrary.getSvgDocument(ARROW); + insertSVGComponentIntoDefsArea(group, obj); + defs.getOwnerDocument().adoptNode(group); + defs.appendChild(group); + + document.adoptNode(defs); + document.getDocumentElement().appendChild(defs); + } + } + + protected void insertSVGComponentIntoDefsArea(Element defs, SVGOMDocument obj) { + for (int i = 0; i < obj.getChildNodes().item(0).getChildNodes().getLength(); i++) { + org.w3c.dom.Node n = obj.getChildNodes().item(0).getChildNodes().item(i).cloneNode(true); + defs.getOwnerDocument().adoptNode(n); + defs.appendChild(n); + } + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSubstationDiagramInitialValueProvider.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSubstationDiagramInitialValueProvider.java new file mode 100644 index 000000000..d63067d3c --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSubstationDiagramInitialValueProvider.java @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import com.powsybl.iidm.network.Branch; +import com.powsybl.iidm.network.Branch.Side; +import com.powsybl.iidm.network.Injection; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.sld.model.BusNode; +import com.powsybl.sld.model.Feeder2WTNode; +import com.powsybl.sld.model.FeederNode; +import com.powsybl.sld.model.Node; +import org.apache.commons.lang3.StringUtils; + +import static com.powsybl.sld.library.ComponentTypeName.BREAKER; +import static com.powsybl.sld.library.ComponentTypeName.BUSBAR_SECTION; +import static com.powsybl.sld.library.ComponentTypeName.CAPACITOR; +import static com.powsybl.sld.library.ComponentTypeName.DISCONNECTOR; +import static com.powsybl.sld.library.ComponentTypeName.GENERATOR; +import static com.powsybl.sld.library.ComponentTypeName.INDUCTOR; +import static com.powsybl.sld.library.ComponentTypeName.LINE; +import static com.powsybl.sld.library.ComponentTypeName.LOAD; +import static com.powsybl.sld.library.ComponentTypeName.LOAD_BREAK_SWITCH; +import static com.powsybl.sld.library.ComponentTypeName.STATIC_VAR_COMPENSATOR; +import static com.powsybl.sld.library.ComponentTypeName.TWO_WINDINGS_TRANSFORMER; +import static com.powsybl.sld.library.ComponentTypeName.VSC_CONVERTER_STATION; + +/** + * @author Giovanni Ferrari + * @author Franck Lecuyer + */ +public class DefaultSubstationDiagramInitialValueProvider implements SubstationDiagramInitialValueProvider { + + private final Network network; + + public DefaultSubstationDiagramInitialValueProvider(Network net) { + network = Objects.requireNonNull(net); + } + + @Override + public InitialValue getInitialValue(Node node) { + Objects.requireNonNull(node); + InitialValue initialValue = new InitialValue(null, null, null, null, null, null); + + if (node.getType() == Node.NodeType.BUS) { + initialValue = new InitialValue(null, null, node.getLabel(), null, null, null); + } else { + String nodeId = node.getId(); + switch (node.getComponentType()) { + case LINE: + case TWO_WINDINGS_TRANSFORMER: + initialValue = getBranchInitialValue(node); + break; + + case LOAD: + initialValue = getLoadInitialValue(network.getLoad(nodeId)); + break; + + case INDUCTOR: + case CAPACITOR: + initialValue = getInjectionInitialValue(network.getShuntCompensator(nodeId)); + break; + + case GENERATOR: + initialValue = getInjectionInitialValue(network.getGenerator(nodeId)); + break; + + case STATIC_VAR_COMPENSATOR: + initialValue = getInjectionInitialValue(network.getStaticVarCompensator(nodeId)); + break; + + case VSC_CONVERTER_STATION: + initialValue = getInjectionInitialValue(network.getVscConverterStation(nodeId)); + break; + + case BUSBAR_SECTION: + case BREAKER: + case LOAD_BREAK_SWITCH: + case DISCONNECTOR: + default: + break; + } + } + return initialValue; + } + + private InitialValue getInjectionInitialValue(Injection injection) { + if (injection != null) { + return new InitialValue(injection); + } else { + return new InitialValue(null, null, null, null, null, null); + } + } + + private InitialValue getLoadInitialValue(Load load) { + if (load != null) { + return new InitialValue(load); + } else { + return new InitialValue(null, null, null, null, null, null); + } + } + + private InitialValue getBranchInitialValue(Node node) { + String nodeId = node.getId(); + if (node instanceof Feeder2WTNode && node.getComponentType().equals(LINE)) { + // special case : branch of threeWindingsTransformer + ThreeWindingsTransformer.Side side = ThreeWindingsTransformer.Side.ONE; + ThreeWindingsTransformer transformer = null; + + int posSide = StringUtils.lastOrdinalIndexOf(nodeId, "_", 1); + if (posSide != -1) { + side = ThreeWindingsTransformer.Side.valueOf(nodeId.substring(posSide + 1)); + posSide = StringUtils.lastOrdinalIndexOf(nodeId, "_", 2); + if (posSide != -1) { + String idTransformer = nodeId.substring(0, posSide); + transformer = network.getThreeWindingsTransformer(idTransformer); + } + } + + if (transformer != null) { + return new InitialValue(transformer, side); + } else { + return new InitialValue(null, null, null, null, null, null); + } + } else { + Branch branch = network.getBranch(nodeId.substring(0, nodeId.length() - 4)); + if (branch != null) { + return new InitialValue(branch, Side.valueOf(nodeId.substring(nodeId.length() - 3))); + } else { + return new InitialValue(null, null, null, null, null, null); + } + } + } + + @Override + public List getNodeLabelValue(Node node) { + Objects.requireNonNull(node); + + List res = new ArrayList<>(); + if (node instanceof FeederNode || node instanceof BusNode) { + res.add(node.getLabel()); + } + return res; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSubstationDiagramStyleProvider.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSubstationDiagramStyleProvider.java new file mode 100644 index 000000000..8504af376 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/DefaultSubstationDiagramStyleProvider.java @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.model.*; + +import java.io.UncheckedIOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import static com.powsybl.sld.svg.SubstationDiagramStyles.WIRE_STYLE_CLASS; +import static com.powsybl.sld.svg.SubstationDiagramStyles.escapeClassName; + +/** + * @author Giovanni Ferrari + * @author Franck Lecuyer + */ +public class DefaultSubstationDiagramStyleProvider implements SubstationDiagramStyleProvider { + + private static final String ARROW1 = ".ARROW1_"; + private static final String ARROW2 = ".ARROW2_"; + private static final String UP = "_UP"; + private static final String DOWN = "_DOWN"; + + @Override + public Optional getNodeStyle(Node node, boolean avoidSVGComponentsDuplication) { + Objects.requireNonNull(node); + if (node.getType() == Node.NodeType.SWITCH && !avoidSVGComponentsDuplication) { + try { + StringBuilder style = new StringBuilder(); + String className = escapeClassName(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name())); + style.append(".").append(className) + .append(" .open { visibility: ").append(node.isOpen() ? "visible;}" : "hidden;}"); + + style.append(".").append(className) + .append(" .closed { visibility: ").append(node.isOpen() ? "hidden;}" : "visible;}"); + + return Optional.of(style.toString()); + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + } + if (node instanceof FeederNode && !avoidSVGComponentsDuplication) { + try { + StringBuilder style = new StringBuilder(); + style.append(ARROW1).append(escapeClassName(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name()))) + .append(UP).append(" .arrow-up {stroke: black; fill: black; fill-opacity:1; visibility: visible;}"); + style.append(ARROW1).append(escapeClassName(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name()))) + .append(UP).append(" .arrow-down { fill-opacity:0; visibility: hidden;}"); + + style.append(ARROW1).append(escapeClassName(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name()))) + .append(DOWN).append(" .arrow-down {stroke: black; fill: black; fill-opacity:1; visibility: visible;}"); + style.append(ARROW1).append(escapeClassName(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name()))) + .append(DOWN).append(" .arrow-up { fill-opacity:0; visibility: hidden;}"); + + style.append(ARROW2).append(escapeClassName(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name()))) + .append(UP).append(" .arrow-up {stroke: blue; fill: blue; fill-opacity:1; visibility: visible;}"); + style.append(ARROW2).append(escapeClassName(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name()))) + .append(UP).append(" .arrow-down { fill-opacity:0; visibility: hidden;}"); + + style.append(ARROW2).append(escapeClassName(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name()))) + .append(DOWN).append(" .arrow-down {stroke: blue; fill: blue; fill-opacity:1; visibility: visible;}"); + style.append(ARROW2).append(escapeClassName(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name()))) + .append(DOWN).append(" .arrow-up { fill-opacity:0; visibility: hidden;}"); + + return Optional.of(style.toString()); + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + } + return Optional.empty(); + } + + @Override + public String getIdWireStyle(Edge edge) { + return WIRE_STYLE_CLASS + "_" + escapeClassName(edge.getNode1().getGraph().getVoltageLevel().getId()); + } + + @Override + public Optional getWireStyle(Edge edge) { + return Optional.empty(); + } + + @Override + public Optional getNode3WTStyle(Fictitious3WTNode node, ThreeWindingsTransformer.Side side) { + return Optional.empty(); + } + + @Override + public Optional getNode2WTStyle(Feeder2WTNode node, TwoWindingsTransformer.Side side) { + return Optional.empty(); + } + + @Override + public Optional getColor(VoltageLevel vl) { + return Optional.empty(); + } + + @Override + public Map getAttributesArrow(int num) { + Map ret = new HashMap<>(); + ret.put("stroke", num == 1 ? "black" : "blue"); + ret.put("fill", num == 1 ? "black" : "blue"); + ret.put("fill-opacity", "1"); + return ret; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/GraphMetadata.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/GraphMetadata.java new file mode 100644 index 000000000..03723675a --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/GraphMetadata.java @@ -0,0 +1,341 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import com.powsybl.commons.json.JsonUtil; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.library.*; +import com.powsybl.sld.model.BusCell; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class GraphMetadata implements AnchorPointProvider { + + public static class NodeMetadata { + + private final String id; + + private String componentType; + + private Double rotationAngle; + + private final boolean open; + + private final String vId; + + private final String nextVId; + + private final BusCell.Direction direction; + + private final boolean vLabel; + + @JsonCreator + public NodeMetadata(@JsonProperty("id") String id, + @JsonProperty("vid") String vId, + @JsonProperty("nextVId") String nextVId, + @JsonProperty("componentType") String componentType, + @JsonProperty("rotationAngle") Double rotationAngle, + @JsonProperty("open") boolean open, + @JsonProperty("direction") BusCell.Direction direction, + @JsonProperty("vlabel") boolean vLabel) { + this.id = Objects.requireNonNull(id); + this.vId = Objects.requireNonNull(vId); + this.nextVId = nextVId; + this.componentType = componentType; + this.rotationAngle = rotationAngle; + this.open = Objects.requireNonNull(open); + this.direction = direction; + this.vLabel = vLabel; + } + + public String getId() { + return id; + } + + public String getVId() { + return vId; + } + + public String getNextVId() { + return nextVId; + } + + public String getComponentType() { + return componentType; + } + + public Double getRotationAngle() { + return rotationAngle; + } + + public boolean isOpen() { + return open; + } + + public BusCell.Direction getDirection() { + return direction; + } + + public boolean isVLabel() { + return vLabel; + } + } + + public static class WireMetadata { + + private final String id; + + private final String nodeId1; + + private final String nodeId2; + + private final boolean straight; + + private final boolean snakeLine; + + @JsonCreator + public WireMetadata(@JsonProperty("id") String id, + @JsonProperty("nodeId1") String nodeId1, + @JsonProperty("nodeId2") String nodeId2, + @JsonProperty("straight") boolean straight, + @JsonProperty("snakeline") boolean snakeline) { + this.id = Objects.requireNonNull(id); + this.nodeId1 = Objects.requireNonNull(nodeId1); + this.nodeId2 = Objects.requireNonNull(nodeId2); + this.straight = straight; + this.snakeLine = snakeline; + } + + public String getId() { + return id; + } + + public String getNodeId1() { + return nodeId1; + } + + public String getNodeId2() { + return nodeId2; + } + + public boolean isStraight() { + return straight; + } + + public boolean isSnakeLine() { + return snakeLine; + } + } + + public static class ArrowMetadata { + + private final String id; + + private final String wireId; + + private final double distance; + + @JsonCreator + public ArrowMetadata(@JsonProperty("id") String id, @JsonProperty("wireId") String wireId1, @JsonProperty("distance") double distance) { + this.id = Objects.requireNonNull(id); + this.wireId = Objects.requireNonNull(wireId1); + this.distance = distance; + } + + public String getId() { + return id; + } + + public String getWireId() { + return wireId; + } + + public double getDistance() { + return distance; + } + } + + private final Map componentMetadataByType = new HashMap<>(); + + private final Map componentMetadataById = new HashMap<>(); + + private final Map nodeMetadataMap = new HashMap<>(); + + private final Map wireMetadataMap = new HashMap<>(); + + private final LayoutParameters layoutParameters; + + private final Map arrowMetadataMap = new HashMap<>(); + + public GraphMetadata() { + this(Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), Collections.emptyList(), new LayoutParameters()); + } + + @JsonCreator + public GraphMetadata(@JsonProperty("components") List componentMetadataList, + @JsonProperty("nodes") List nodeMetadataList, + @JsonProperty("wires") List wireMetadataList, + @JsonProperty("arrows") List arrowMetadataList, + @JsonProperty("layoutParams") LayoutParameters layoutParams) { + for (ComponentMetadata componentMetadata : componentMetadataList) { + addComponentMetadata(componentMetadata); + } + for (NodeMetadata nodeMetadata : nodeMetadataList) { + addNodeMetadata(nodeMetadata); + } + for (WireMetadata wireMetadata : wireMetadataList) { + addWireMetadata(wireMetadata); + } + for (ArrowMetadata arrowMetadata : arrowMetadataList) { + addArrowMetadata(arrowMetadata); + } + layoutParameters = layoutParams; + } + + public static GraphMetadata parseJson(Path file) { + try (Reader reader = Files.newBufferedReader(file)) { + return parseJson(reader); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static GraphMetadata parseJson(InputStream inputStream) { + Objects.requireNonNull(inputStream); + ObjectMapper objectMapper = JsonUtil.createObjectMapper(); + try { + return objectMapper.readValue(inputStream, GraphMetadata.class); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static GraphMetadata parseJson(Reader reader) { + Objects.requireNonNull(reader); + ObjectMapper objectMapper = JsonUtil.createObjectMapper(); + try { + return objectMapper.readValue(reader, GraphMetadata.class); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public void writeJson(Path file) { + Objects.requireNonNull(file); + try (Writer writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8)) { + writeJson(writer); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public void writeJson(Writer writer) { + Objects.requireNonNull(writer); + ObjectMapper objectMapper = JsonUtil.createObjectMapper(); + try { + objectMapper.writerWithDefaultPrettyPrinter() + .writeValue(writer, this); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public void addComponentMetadata(ComponentMetadata metadata) { + Objects.requireNonNull(metadata); + componentMetadataByType.put(metadata.getType(), metadata); + if (metadata.getId() != null) { + componentMetadataById.put(metadata.getId(), metadata); + } + } + + public ComponentMetadata getComponentMetadata(String componentType) { + return componentType != null ? componentMetadataByType.get(componentType) : null; + } + + @Override + public List getAnchorPoints(String type, String id) { + ComponentMetadata componentMetadata = null; + if (id != null) { + componentMetadata = componentMetadataById.get(id); + } + if (componentMetadata == null) { + componentMetadata = getComponentMetadata(type); + } + return componentMetadata != null ? componentMetadata.getAnchorPoints() + : Collections.singletonList(new AnchorPoint(0, 0, AnchorOrientation.NONE)); + } + + @JsonProperty("components") + public List getComponentMetadata() { + return ImmutableList.copyOf(componentMetadataByType.values()); + } + + public void addNodeMetadata(NodeMetadata metadata) { + Objects.requireNonNull(metadata); + nodeMetadataMap.put(metadata.getId(), metadata); + } + + public NodeMetadata getNodeMetadata(String id) { + Objects.requireNonNull(id); + return nodeMetadataMap.get(id); + } + + @JsonProperty("nodes") + public List getNodeMetadata() { + return ImmutableList.copyOf(nodeMetadataMap.values()); + } + + public void addWireMetadata(WireMetadata metadata) { + Objects.requireNonNull(metadata); + wireMetadataMap.put(metadata.getId(), metadata); + } + + public WireMetadata getWireMetadata(String id) { + Objects.requireNonNull(id); + return wireMetadataMap.get(id); + } + + @JsonProperty("wires") + public List getWireMetadata() { + return ImmutableList.copyOf(wireMetadataMap.values()); + } + + public void addArrowMetadata(ArrowMetadata metadata) { + Objects.requireNonNull(metadata); + arrowMetadataMap.put(metadata.getId(), metadata); + } + + public ArrowMetadata getArrowMetadata(String id) { + Objects.requireNonNull(id); + return arrowMetadataMap.get(id); + } + + @JsonProperty("arrows") + public List getArrowMetadata() { + return ImmutableList.copyOf(arrowMetadataMap.values()); + } + + @JsonProperty("layoutParams") + public LayoutParameters getLayoutParameters() { + return layoutParameters; + + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/InitialValue.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/InitialValue.java new file mode 100644 index 000000000..9a7d653e2 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/InitialValue.java @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import java.util.Objects; +import java.util.Optional; + +import com.powsybl.iidm.network.Branch; +import com.powsybl.iidm.network.Branch.Side; +import com.powsybl.iidm.network.Injection; +import com.powsybl.iidm.network.Load; +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.sld.svg.SubstationDiagramInitialValueProvider.Direction; + +/** + * @author Giovanni Ferrari + */ +public class InitialValue { + + private final Direction direction1; + private final Direction direction2; + private final String label1; + private final String label2; + private final String label3; + private final String label4; + + public InitialValue(Direction dir1, Direction dir2, String text1, String text2, String text3, String text4) { + direction1 = dir1; + direction2 = dir2; + label1 = text1; + label2 = text2; + label3 = text3; + label4 = text4; + } + + public InitialValue(Branch ln, Side side) { + Objects.requireNonNull(ln); + Objects.requireNonNull(side); + double p = side.equals(Side.ONE) ? ln.getTerminal1().getP() : ln.getTerminal2().getP(); + double q = side.equals(Side.ONE) ? ln.getTerminal1().getQ() : ln.getTerminal2().getQ(); + label1 = String.valueOf(Math.round(p)); + label2 = String.valueOf(Math.round(q)); + label3 = null; + label4 = null; + direction1 = p > 0 ? Direction.UP : Direction.DOWN; + direction2 = q > 0 ? Direction.UP : Direction.DOWN; + } + + public InitialValue(Load load) { + Objects.requireNonNull(load); + double p = load.getP0(); + double q = load.getQ0(); + label1 = String.valueOf(Math.round(p)); + label2 = String.valueOf(Math.round(q)); + label3 = null; + label4 = null; + direction1 = p > 0 ? Direction.UP : Direction.DOWN; + direction2 = q > 0 ? Direction.UP : Direction.DOWN; + } + + public InitialValue(Injection injection) { + Objects.requireNonNull(injection); + double p = injection.getTerminal().getP(); + double q = injection.getTerminal().getQ(); + label1 = String.valueOf(Math.round(p)); + label2 = String.valueOf(Math.round(q)); + label3 = null; + label4 = null; + direction1 = p > 0 ? Direction.UP : Direction.DOWN; + direction2 = q > 0 ? Direction.UP : Direction.DOWN; + } + + public InitialValue(ThreeWindingsTransformer transformer, ThreeWindingsTransformer.Side side) { + Objects.requireNonNull(transformer); + Objects.requireNonNull(side); + double p = transformer.getTerminal(side).getP(); + double q = transformer.getTerminal(side).getQ(); + label1 = String.valueOf(Math.round(p)); + label2 = String.valueOf(Math.round(q)); + label3 = null; + label4 = null; + direction1 = p > 0 ? Direction.UP : Direction.DOWN; + direction2 = q > 0 ? Direction.UP : Direction.DOWN; + } + + public Optional getArrowDirection1() { + return Optional.ofNullable(direction1); + } + + public Optional getArrowDirection2() { + return Optional.ofNullable(direction2); + } + + public Optional getLabel1() { + return Optional.ofNullable(label1); + } + + public Optional getLabel2() { + return Optional.ofNullable(label2); + } + + public Optional getLabel3() { + return Optional.ofNullable(label3); + } + + public Optional getLabel4() { + return Optional.ofNullable(label4); + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/LabelPosition.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/LabelPosition.java new file mode 100644 index 000000000..8e8ddbde7 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/LabelPosition.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import java.util.Objects; + +/** + * @author Franck Lecuyer + */ +public class LabelPosition { + + private final String positionName; + + private final double dX; + + private final double dY; + + public LabelPosition(String positionName, double dX, double dY) { + this.positionName = Objects.requireNonNull(positionName); + this.dX = dX; + this.dY = dY; + } + + public String getPositionName() { + return positionName; + } + + public double getdX() { + return dX; + } + + public double getdY() { + return dY; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/NodeLabelConfiguration.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/NodeLabelConfiguration.java new file mode 100644 index 000000000..ce1b0ab42 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/NodeLabelConfiguration.java @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.powsybl.sld.model.Node; + +import java.util.List; + +/** + * @author Franck Lecuyer + */ +public interface NodeLabelConfiguration { + List getLabelsPosition(Node node); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SVGLoaderToDocument.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SVGLoaderToDocument.java new file mode 100644 index 000000000..1358ae866 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SVGLoaderToDocument.java @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import org.apache.batik.anim.dom.SAXSVGDocumentFactory; +import org.apache.batik.anim.dom.SVGOMDocument; +import org.apache.batik.bridge.*; +import org.apache.batik.util.XMLResourceDescriptor; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class SVGLoaderToDocument { + + /** + * Read a file in svg format and return the SVGDocument corresponding + * + * @param fileName filename + * @return document + */ + public SVGOMDocument read(String fileName) { + try (InputStream svgFile = this.getClass().getResourceAsStream(fileName)) { + String parser = XMLResourceDescriptor.getXMLParserClassName(); + SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser); + SVGOMDocument document = (SVGOMDocument) factory.createDocument("", svgFile); + + UserAgent userAgent = new UserAgentAdapter(); + DocumentLoader loader = new DocumentLoader(userAgent); + BridgeContext bridgeContext = new BridgeContext(userAgent, loader); + bridgeContext.setDynamicState(BridgeContext.DYNAMIC); + + // Enable CSS- and SVG-specific enhancements. + (new GVTBuilder()).build(bridgeContext, document); + + return document; + } catch (IOException e) { + throw new UncheckedIOException("Can't read svg file from the SVG library!", e); + } + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SVGWriter.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SVGWriter.java new file mode 100644 index 000000000..aefb70469 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SVGWriter.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.SubstationGraph; + +import java.io.Writer; +import java.nio.file.Path; + +/** + * @author Gilles Brada + * @author Franck Lecuyer + */ +public interface SVGWriter { + + GraphMetadata write(String prefixId, + Graph graph, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Path svgFile); + + GraphMetadata write(String prefixId, + Graph graph, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Writer writer); + + GraphMetadata write(String prefixId, + SubstationGraph graph, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Path svgFile); + + GraphMetadata write(String prefixId, + SubstationGraph graph, + SubstationDiagramInitialValueProvider initProvider, + SubstationDiagramStyleProvider styleProvider, + NodeLabelConfiguration nodeLabelConfiguration, + Writer writer); + + LayoutParameters getLayoutParameters(); + + ComponentLibrary getComponentLibrary(); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramInitialValueProvider.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramInitialValueProvider.java new file mode 100644 index 000000000..136fa5397 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramInitialValueProvider.java @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.powsybl.sld.model.Node; + +import java.util.List; + +/** + * @author Giovanni Ferrari + * @author Franck Lecuyer + */ +public interface SubstationDiagramInitialValueProvider { + + public enum Direction { + UP, DOWN; + } + + InitialValue getInitialValue(Node node); + + List getNodeLabelValue(Node node); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramStyleProvider.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramStyleProvider.java new file mode 100644 index 000000000..4c1245fff --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramStyleProvider.java @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.model.Edge; +import com.powsybl.sld.model.Feeder2WTNode; +import com.powsybl.sld.model.Fictitious3WTNode; +import com.powsybl.sld.model.Node; + +import java.util.Map; +import java.util.Optional; + +/** + * @author Giovanni Ferrari + * @author Franck Lecuyer + */ +public interface SubstationDiagramStyleProvider { + + Optional getNodeStyle(Node node, boolean avoidSVGComponentsDuplication); + + String getIdWireStyle(Edge edge); + + Optional getWireStyle(Edge edge); + + Optional getNode3WTStyle(Fictitious3WTNode node, ThreeWindingsTransformer.Side side); + + Optional getNode2WTStyle(Feeder2WTNode node, TwoWindingsTransformer.Side side); + + Optional getColor(VoltageLevel vl); + + Map getAttributesArrow(int num); +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramStyles.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramStyles.java new file mode 100644 index 000000000..97e1e4352 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/SubstationDiagramStyles.java @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Objects; +import java.util.regex.Pattern; + +/** + * @author Geoffroy Jamgotchian + */ +public final class SubstationDiagramStyles { + + public static final String WIRE_STYLE_CLASS = "wire"; + public static final String GRID_STYLE_CLASS = "grid"; + public static final String BUS_STYLE_CLASS = "busbar-section"; + public static final String LABEL_STYLE_CLASS = "component-label"; + + private SubstationDiagramStyles() { + } + + public static String escapeClassName(String input) { + Objects.requireNonNull(input); + String temp = input; + // class name length should be at least 2 + if (temp.length() < 2) { + temp = StringUtils.leftPad(temp, 2, "_"); + } + return escapeId(temp); + } + + public static String escapeId(String input) { + Objects.requireNonNull(input); + String temp = input; + // class name cannot start with a digit + temp = Character.isDigit(temp.charAt(0)) ? "d" + temp : temp; + // class name cannot begin with two hyphens or a hyphen followed by a digit + if (temp.startsWith("--") || (temp.charAt(0) == '-' && Character.isDigit(temp.charAt(1)))) { + temp = "d" + temp; + } + // Substitution of all non authorized characters + return Pattern.compile("[^\\_\\-a-zA-Z0-9][^\\_\\-a-zA-Z0-9]*", 32).matcher(temp).replaceAll("_"); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/WireConnection.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/WireConnection.java new file mode 100644 index 000000000..bd58e0a64 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/svg/WireConnection.java @@ -0,0 +1,142 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.powsybl.sld.library.AnchorOrientation; +import com.powsybl.sld.library.AnchorPoint; +import com.powsybl.sld.library.AnchorPointProvider; +import com.powsybl.sld.model.BaseNode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class WireConnection { + + private AnchorPoint anchorPoint1; + + private AnchorPoint anchorPoint2; + + WireConnection(AnchorPoint anchorPoint1, AnchorPoint anchorPoint2) { + this.anchorPoint1 = Objects.requireNonNull(anchorPoint1); + this.anchorPoint2 = Objects.requireNonNull(anchorPoint2); + } + + /** + * Calculates the distance between two points. + * + * @param x1 x1 + * @param y1 y1 + * @param x2 x2 + * @param y2 y2 + * @return distance + */ + private static double calculateDistancePoint(double x1, double y1, double x2, double y2) { + return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1); + } + + private static List getAnchorPoints(AnchorPointProvider anchorPointProvider, BaseNode node) { + return anchorPointProvider.getAnchorPoints(node.getComponentType(), node.getId()) + .stream() + .map(anchorPoint -> node.isRotated() ? anchorPoint.rotate(node.getRotationAngle()) : anchorPoint) + .collect(Collectors.toList()); + } + + public static WireConnection searchBetterAnchorPoints(AnchorPointProvider anchorPointProvider, + BaseNode node1, + BaseNode node2) { + Objects.requireNonNull(anchorPointProvider); + Objects.requireNonNull(node1); + Objects.requireNonNull(node2); + + List anchorPoints1 = getAnchorPoints(anchorPointProvider, node1); + List anchorPoints2 = getAnchorPoints(anchorPointProvider, node2); + AnchorPoint betterAnchorPoint1 = anchorPoints1.get(0); + AnchorPoint betterAnchorPoint2 = anchorPoints2.get(0); + + double currentDistance = calculateDistancePoint(node1.getX() + betterAnchorPoint1.getX(), + node1.getY() + betterAnchorPoint1.getY(), + node2.getX() + betterAnchorPoint2.getX(), + node2.getY() + betterAnchorPoint2.getY()); + + for (AnchorPoint anchorPoint1 : anchorPoints1) { + for (AnchorPoint anchorPoint2 : anchorPoints2) { + double distance = calculateDistancePoint(node1.getX() + anchorPoint1.getX(), + node1.getY() + anchorPoint1.getY(), + node2.getX() + anchorPoint2.getX(), + node2.getY() + anchorPoint2.getY()); + if (distance < currentDistance) { + betterAnchorPoint1 = anchorPoint1; + betterAnchorPoint2 = anchorPoint2; + currentDistance = distance; + } + } + } + + return new WireConnection(betterAnchorPoint1, betterAnchorPoint2); + } + + public AnchorPoint getAnchorPoint1() { + return anchorPoint1; + } + + public AnchorPoint getAnchorPoint2() { + return anchorPoint2; + } + + /* + * Calculating the polyline points for the voltageLevel graph edge + */ + public List calculatePolylinePoints(BaseNode node1, BaseNode node2, boolean straight) { + double x1 = node1.getX() + getAnchorPoint1().getX(); + double y1 = node1.getY() + getAnchorPoint1().getY(); + double x2 = node2.getX() + getAnchorPoint2().getX(); + double y2 = node2.getY() + getAnchorPoint2().getY(); + + if (straight || (x1 == x2 || y1 == y2)) { + return Arrays.asList(x1, y1, x2, y2); + } + List pol = new ArrayList<>(); + switch (anchorPoint1.getOrientation()) { + case VERTICAL: + if (anchorPoint2.getOrientation() == AnchorOrientation.VERTICAL) { + double mid = (y1 + y2) / 2; + pol.addAll(Arrays.asList(x1, y1, x1, mid, x2, mid, x2, y2)); + } else { + pol.addAll(Arrays.asList(x1, y1, x1, y2, x2, y2)); + } + break; + case HORIZONTAL: + if (anchorPoint2.getOrientation() == AnchorOrientation.HORIZONTAL) { + double mid = (x1 + x2) / 2; + pol.addAll(Arrays.asList(x1, y1, mid, y1, mid, y2, x2, y2)); + } else { + pol.addAll(Arrays.asList(x1, y1, x2, y1, x2, y2)); + } + break; + case NONE: + // Case none-none is not handled, it never happens (even if it happen it will execute another case) + if (anchorPoint2.getOrientation() == AnchorOrientation.HORIZONTAL) { + pol.addAll(Arrays.asList(x1, y1, x1, y2, x2, y2)); + } else { + pol.addAll(Arrays.asList(x1, y1, x2, y1, x2, y2)); + } + break; + default: + break; + } + return pol; + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/util/NominalVoltageSubstationDiagramStyleProvider.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/util/NominalVoltageSubstationDiagramStyleProvider.java new file mode 100644 index 000000000..3748d9918 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/util/NominalVoltageSubstationDiagramStyleProvider.java @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.util; + +import com.powsybl.iidm.network.ThreeWindingsTransformer; +import com.powsybl.iidm.network.TwoWindingsTransformer; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.model.Edge; +import com.powsybl.sld.model.Feeder2WTNode; +import com.powsybl.sld.model.Fictitious3WTNode; +import com.powsybl.sld.model.Node; +import com.powsybl.sld.svg.DefaultSubstationDiagramStyleProvider; + +import java.io.UncheckedIOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Optional; + +import static com.powsybl.sld.svg.SubstationDiagramStyles.*; + +/** + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class NominalVoltageSubstationDiagramStyleProvider extends DefaultSubstationDiagramStyleProvider { + + private static final String DEFAULT_COLOR = "rgb(171, 175, 40)"; + + @Override + public Optional getColor(VoltageLevel vl) { + String color; + if (vl.getNominalV() >= 300) { + color = "rgb(255, 0, 0)"; + } else if (vl.getNominalV() >= 170 && vl.getNominalV() < 300) { + color = "rgb(34, 139, 34)"; + } else if (vl.getNominalV() >= 120 && vl.getNominalV() < 170) { + color = "rgb(1, 175, 175)"; + } else if (vl.getNominalV() >= 70 && vl.getNominalV() < 120) { + color = "rgb(204, 85, 0)"; + } else if (vl.getNominalV() >= 50 && vl.getNominalV() < 70) { + color = "rgb(160, 32, 240)"; + } else if (vl.getNominalV() >= 30 && vl.getNominalV() < 50) { + color = "rgb(255, 130, 144)"; + } else { + color = DEFAULT_COLOR; + } + return Optional.of(color); + } + + @Override + public Optional getNodeStyle(Node node, boolean avoidSVGComponentsDuplication) { + Optional defaultStyle = super.getNodeStyle(node, avoidSVGComponentsDuplication); + + String color = getColor(node.getGraph().getVoltageLevel()).orElse(DEFAULT_COLOR); + try { + if (node.getType() == Node.NodeType.SWITCH) { + return defaultStyle; + } else { + return Optional.of(defaultStyle.orElse("") + " ." + escapeId(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name())) + " {stroke:" + color + ";}"); + } + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public String getIdWireStyle(Edge edge) { + Node node1 = edge.getNode1(); + Node node2 = edge.getNode2(); + if ((node1 instanceof Fictitious3WTNode && node2 instanceof Feeder2WTNode) || + (node1 instanceof Feeder2WTNode && node2 instanceof Fictitious3WTNode)) { + VoltageLevel vl = node1 instanceof Feeder2WTNode ? ((Feeder2WTNode) node1).getVlOtherSide() : ((Feeder2WTNode) node2).getVlOtherSide(); + return WIRE_STYLE_CLASS + "_" + escapeClassName(vl.getId()); + } else { + return WIRE_STYLE_CLASS + "_" + escapeClassName(edge.getNode1().getGraph().getVoltageLevel().getId()); + } + } + + @Override + public Optional getWireStyle(Edge edge) { + Node node1 = edge.getNode1(); + Node node2 = edge.getNode2(); + VoltageLevel vl; + if ((node1 instanceof Fictitious3WTNode && node2 instanceof Feeder2WTNode) || + (node1 instanceof Feeder2WTNode && node2 instanceof Fictitious3WTNode)) { + vl = node1 instanceof Feeder2WTNode ? ((Feeder2WTNode) node1).getVlOtherSide() : ((Feeder2WTNode) node2).getVlOtherSide(); + } else { + vl = edge.getNode1().getGraph().getVoltageLevel(); + } + String idVL = escapeClassName(vl.getId()); + String color = getColor(vl).orElse(DEFAULT_COLOR); + StringBuilder style = new StringBuilder(); + style.append(".").append(WIRE_STYLE_CLASS).append("_").append(idVL).append(" {stroke:").append(color).append(";stroke-width:1;}"); + return Optional.of(style.toString()); + } + + @Override + public Optional getNode3WTStyle(Fictitious3WTNode node, ThreeWindingsTransformer.Side side) { + return getColor(node.getTransformer().getTerminal(side).getVoltageLevel()); + } + + @Override + public Optional getNode2WTStyle(Feeder2WTNode node, TwoWindingsTransformer.Side side) { + return getColor(side == TwoWindingsTransformer.Side.ONE ? node.getGraph().getVoltageLevel() : node.getVlOtherSide()); + } +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/util/RGBColor.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/util/RGBColor.java new file mode 100644 index 000000000..4a62b8713 --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/util/RGBColor.java @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author Giovanni Ferrari + */ +public class RGBColor { + + private int red; + private int green; + private int blue; + + public RGBColor(int r, int g, int b) { + red = r; + green = g; + blue = b; + } + + public static RGBColor parse(String color) { + Objects.requireNonNull(color); + + if (color.length() < 7) { + throw new IllegalArgumentException("Invalid length"); + } + + int r = Integer.parseInt(color.substring(1, 3), 16); + int g = Integer.parseInt(color.substring(3, 5), 16); + int b = Integer.parseInt(color.substring(5, 7), 16); + return new RGBColor(r, g, b); + } + + public int getRed() { + return red; + } + + public void setRed(int red) { + this.red = red; + } + + public int getGreen() { + return green; + } + + public void setGreen(int green) { + this.green = green; + } + + public int getBlue() { + return blue; + } + + public void setBlue(int blue) { + this.blue = blue; + } + + public RGBColor getBrighter(double factor) { + int x = (int) (1.0 / (1.0 - factor)); + if (red == 0 && green == 0 && blue == 0) { + return new RGBColor(x, x, x); + } + int r = red; + int g = green; + int b = blue; + if (red > 0 && red < x) { + r = x; + } + if (green > 0 && green < x) { + g = x; + } + if (blue > 0 && b < x) { + b = x; + } + return new RGBColor(Math.min((int) (r / factor), 255), Math.min((int) (g / factor), 255), Math.min((int) (b / factor), 255)); + } + + public RGBColor getDarker(double factor) { + return new RGBColor(Math.max((int) (red * factor), 0), Math.max((int) (green * factor), 0), Math.max((int) (blue * factor), 0)); + } + + public List getColorGradient(int steps, double factor) { + ArrayList gradient = new ArrayList(); + + RGBColor c1 = getBrighter(factor); + RGBColor c2 = getDarker(factor); + + for (int i = 0; i < steps; i++) { + float ratio = (float) i / (float) steps; + int r = (int) (c2.getRed() * ratio + c1.getRed() * (1 - ratio)); + int g = (int) (c2.getGreen() * ratio + c1.getGreen() * (1 - ratio)); + int b = (int) (c2.getBlue() * ratio + c1.getBlue() * (1 - ratio)); + RGBColor c = new RGBColor(r, g, b); + gradient.add(c); + } + return gradient; + } + + public String toString() { + return String.format("#%02X%02X%02X", red, green, blue); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + blue; + result = prime * result + green; + result = prime * result + red; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + RGBColor other = (RGBColor) obj; + return blue == other.blue && green == other.green && red == other.red; + } + +} diff --git a/single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologicalStyleProvider.java b/single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologicalStyleProvider.java new file mode 100644 index 000000000..68967fb2c --- /dev/null +++ b/single-line-diagram-core/src/main/java/com/powsybl/sld/util/TopologicalStyleProvider.java @@ -0,0 +1,218 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.util; + +import com.powsybl.basevoltage.BaseVoltageColor; +import com.powsybl.iidm.network.Branch.Side; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.model.Edge; +import com.powsybl.sld.model.Node; +import com.powsybl.sld.model.Node.NodeType; +import com.powsybl.sld.svg.DefaultSubstationDiagramStyleProvider; +import com.powsybl.sld.svg.SubstationDiagramStyles; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; + +import static com.powsybl.sld.library.ComponentTypeName.*; +import static com.powsybl.sld.svg.SubstationDiagramStyles.escapeId; + + +/** + * @author Giovanni Ferrari + */ +public class TopologicalStyleProvider extends DefaultSubstationDiagramStyleProvider { + + private BaseVoltageColor baseVoltageColor; + private HashMap> voltageLevelColorMap = new HashMap(); + private static final String DEFAULT_COLOR = "#FF0000"; + private static final String DISCONNECTED_COLOR = "#808080"; + private static final double FACTOR = 0.7; + private String disconnectedColor; + + public TopologicalStyleProvider(Path config) { + try { + baseVoltageColor = config != null ? new BaseVoltageColor(config) : new BaseVoltageColor(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + disconnectedColor = getBaseColor(0, "RTE", DISCONNECTED_COLOR); + } + + private RGBColor getBusColor(Node node) { + String id = node.getId(); + VoltageLevel vl = node.getGraph().getVoltageLevel(); + return voltageLevelColorMap.computeIfAbsent(vl.getId(), k -> getColorMap(vl)).get(id); + } + + private HashMap getColorMap(VoltageLevel vl) { + String basecolor = getBaseColor(vl.getNominalV(), "RTE", DEFAULT_COLOR); + + AtomicInteger idxColor = new AtomicInteger(0); + long buses = vl.getBusView().getBusStream().count(); + + HashMap colorMap = new HashMap(); + HashMap busColorMap = new HashMap(); + + RGBColor color = RGBColor.parse(basecolor); + + List colors = color.getColorGradient((int) buses, FACTOR); + + vl.getBusView().getBuses().forEach(b -> { + RGBColor c = colors.get(idxColor.getAndIncrement()); + busColorMap.put(b.getId(), c); + + vl.getBusView().getBus(b.getId()).visitConnectedEquipments(new TopologyVisitor() { + @Override + public void visitBusbarSection(BusbarSection e) { + colorMap.put(e.getId(), c); + } + + @Override + public void visitDanglingLine(DanglingLine e) { + colorMap.put(e.getId(), c); + } + + @Override + public void visitGenerator(Generator e) { + colorMap.put(e.getId(), c); + } + + @Override + public void visitLine(Line e, Side s) { + colorMap.put(e.getId(), c); + } + + @Override + public void visitLoad(Load e) { + colorMap.put(e.getId(), c); + } + + @Override + public void visitShuntCompensator(ShuntCompensator e) { + colorMap.put(e.getId(), c); + } + + @Override + public void visitStaticVarCompensator(StaticVarCompensator e) { + colorMap.put(e.getId(), c); + } + + @Override + public void visitThreeWindingsTransformer(ThreeWindingsTransformer e, + com.powsybl.iidm.network.ThreeWindingsTransformer.Side s) { + colorMap.put(e.getId(), c); + } + + @Override + public void visitTwoWindingsTransformer(TwoWindingsTransformer e, Side s) { + colorMap.put(e.getId(), c); + } + }); + }); + return colorMap; + } + + private String getBaseColor(double v, String profile, String defaultColor) { + return baseVoltageColor.getColor(v, profile) != null ? baseVoltageColor.getColor(v, profile) : defaultColor; + + } + + @Override + public Optional getNodeStyle(Node node, boolean avoidSVGComponentsDuplication) { + + Optional defaultStyle = super.getNodeStyle(node, avoidSVGComponentsDuplication); + if (node.getType() == NodeType.SWITCH || node.getComponentType().equals(TWO_WINDINGS_TRANSFORMER) || node.getComponentType().equals(THREE_WINDINGS_TRANSFORMER) || node.getComponentType().equals(PHASE_SHIFT_TRANSFORMER)) { + return defaultStyle; + } + + try { + RGBColor c = getBusColor(node); + + String color = c != null ? c.toString() : disconnectedColor; + + return Optional.of(defaultStyle.orElse("") + " #" + + escapeId(URLEncoder.encode(node.getId(), StandardCharsets.UTF_8.name())) + " {stroke:" + + color + ";}"); + + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + } + + @Override + public Optional getWireStyle(Edge edge) { + + try { + String wireId = SubstationDiagramStyles + .escapeId(URLEncoder.encode( + edge.getNode1().getGraph().getVoltageLevel().getId() + "_Wire" + + edge.getNode1().getGraph().getEdges().indexOf(edge), + StandardCharsets.UTF_8.name())); + Node bus = findConnectedBus(edge); + String color = disconnectedColor; + if (bus != null) { + RGBColor c = getBusColor(bus); + if (c != null) { + color = c.toString(); + } + } + + return Optional.of(" #" + wireId + " {stroke:" + color + ";}"); + } catch (UnsupportedEncodingException e) { + throw new UncheckedIOException(e); + } + } + + private Node findConnectedBus(Edge edge) { + Node n1 = edge.getNode1(); + if (n1.getType() == NodeType.BUS) { + return n1; + } + Node n2 = edge.getNode2(); + if (n1.getType() == NodeType.BUS) { + return n2; + } + Node n11 = findConnectedBus(n1, new ArrayList()); + if (n11 != null) { + return n11; + } else { + return findConnectedBus(n2, new ArrayList()); + } + } + + private Node findConnectedBus(Node node, List visitedNodes) { + List nodesToVisit = new ArrayList<>(node.getAdjacentNodes()); + if (!visitedNodes.contains(node)) { + visitedNodes.add(node); + if (node.getType().equals(NodeType.SWITCH) && node.isOpen()) { + return null; + } + for (Node n : nodesToVisit) { + if (n.getType().equals(NodeType.BUS)) { + return n; + } else { + Node n1 = findConnectedBus(n, visitedNodes); + if (n1 != null) { + return n1; + } + } + } + } + return null; + } + +} diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/2-windings-transformer.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/2-windings-transformer.svg new file mode 100644 index 000000000..8a1877556 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/2-windings-transformer.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/3-windings-transformer.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/3-windings-transformer.svg new file mode 100644 index 000000000..693cf7e9b --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/3-windings-transformer.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/arrow.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/arrow.svg new file mode 100644 index 000000000..37d992474 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/arrow.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/breaker.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/breaker.svg new file mode 100644 index 000000000..af8157146 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/breaker.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/capacitor.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/capacitor.svg new file mode 100644 index 000000000..5795ed7c4 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/capacitor.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/components.css b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/components.css new file mode 100644 index 000000000..20d4ae803 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/components.css @@ -0,0 +1,110 @@ +.BUSBAR_SECTION { + stroke: rgb(0,0,0); + stroke-width: 3; +} + +.BREAKER { + stroke: rgb(0, 0, 255); + stroke-width: 2; + fill: rgb(255, 255, 255); +} + +.DISCONNECTOR { + stroke: rgb(0, 0, 0); + stroke-width: 3 +} + +.GENERATOR { + stroke: rgb(0, 0, 255); + stroke-width: 1; + fill: rgb(255, 255, 255); +} + +.LOAD { + stroke: rgb(0, 0, 255); + stroke-width: 1; + fill: rgb(255, 255, 255); +} + +.LOAD_BREAK_SWITCH { + stroke: rgb(0, 0, 255); + stroke-width: 1; + fill: rgb(255, 255, 255); +} + +.NODE { + stroke: rgb(0, 0, 0); + fill: rgb(0, 0, 0); + fill-opacity: 1; +} + +.CAPACITOR { + stroke: rgb(0, 0, 255); + stroke-width: 1; + fill-opacity: 0; +} + +.INDUCTOR { + stroke: rgb(0, 0, 255); + stroke-width: 1; + fill-opacity: 0; +} + +.STATIC_VAR_COMPENSATOR { + stroke: rgb(0, 0, 255); +} + +.TWO_WINDINGS_TRANSFORMER { + stroke: rgb(0, 0, 255); + fill-opacity: 0; +} + +.THREE_WINDINGS_TRANSFORMER { + stroke: rgb(0, 0, 255); + fill-opacity: 0; +} + +.VSC_CONVERTER_STATION { + stroke: rgb(0, 0, 255); + font-size: 7.4314661px; + line-height: 1.25; + font-family: sans-serif; + font-variant-ligatures: normal; + font-variant-caps: normal; + font-variant-numeric: normal; + font-feature-settings: normal; + text-align: start; + letter-spacing: 0px; + word-spacing: 0px; + writing-mode: lr-tb; + text-anchor: start; + stroke-miterlimit: 4; +} + +.PHASE_SHIFT_TRANSFORMER { + stroke: rgb(0, 0, 255); + stroke-linecap: butt; + stroke-linejoin: miter; + stroke-miterlimit: 4; + stroke-width: 1; + fill: rgb(255, 255, 255); +} + +.wire { + stroke: rgb(200,0,0); + stroke-width: 1; + fill-opacity: 0; +} + +.grid { + stroke: rgb(0,55,0); + stroke-width: 1; + stroke-dasharray: 1,10; +} + +.component-label { + fill: black; + color: black; + stroke: none; + fill-opacity: 1 +} diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/components.xml b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/components.xml new file mode 100644 index 000000000..556b47b28 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/components.xml @@ -0,0 +1,117 @@ + + + + breaker.svg + + + + + + + + load-break-switch.svg + + + + + + + + load.svg + + + + + + + + generator.svg + + + + + + + + + + 2-windings-transformer.svg + + + + + + + + + + 3-windings-transformer.svg + + + + + + + + + capacitor.svg + + + + + + + + inductor.svg + + + + + + + + disconnector.svg + + + + + + + node.svg + + + + + + + phaseShift.svg + + + + + + + + svc.svg + + + + + + + + vsc.svg + + + + + + + + arrow.svg + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/disconnector.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/disconnector.svg new file mode 100644 index 000000000..7913d808c --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/disconnector.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/generator.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/generator.svg new file mode 100644 index 000000000..6212708de --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/generator.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/inductor.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/inductor.svg new file mode 100644 index 000000000..6840af87a --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/inductor.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/load-break-switch.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/load-break-switch.svg new file mode 100644 index 000000000..28b682027 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/load-break-switch.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/load.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/load.svg new file mode 100644 index 000000000..01630b582 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/load.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/node.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/node.svg new file mode 100644 index 000000000..61cb5375e --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/node.svg @@ -0,0 +1,4 @@ + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/phaseShift.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/phaseShift.svg new file mode 100644 index 000000000..f1101e744 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/phaseShift.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/svc.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/svc.svg new file mode 100644 index 000000000..61e389599 --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/svc.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/single-line-diagram-core/src/main/resources/ConvergenceLibrary/vsc.svg b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/vsc.svg new file mode 100644 index 000000000..b8d5efdbf --- /dev/null +++ b/single-line-diagram-core/src/main/resources/ConvergenceLibrary/vsc.svg @@ -0,0 +1,7 @@ + + + + AC / DC + + + diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/AbstractTestCase.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/AbstractTestCase.java new file mode 100644 index 000000000..6fe434efb --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/AbstractTestCase.java @@ -0,0 +1,155 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.google.common.io.ByteStreams; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Substation; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.SubstationGraph; +import com.powsybl.sld.svg.DefaultNodeLabelConfiguration; +import com.powsybl.sld.svg.DefaultSubstationDiagramInitialValueProvider; +import com.powsybl.sld.svg.DefaultSubstationDiagramStyleProvider; +import com.powsybl.sld.svg.DefaultSVGWriter; +import com.powsybl.sld.svg.SubstationDiagramStyleProvider; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; + +import static org.junit.Assert.assertEquals; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public abstract class AbstractTestCase { + + protected Network network; + + protected VoltageLevel vl; + protected Substation substation; + + protected final ResourcesComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + + protected final SubstationDiagramStyleProvider styleProvider = new DefaultSubstationDiagramStyleProvider(); + + protected static String normalizeLineSeparator(String str) { + return str.replace("\r\n", "\n") + .replace("\r", "\n"); + } + + abstract void setUp() throws IOException; + + String getName() { + return getClass().getSimpleName(); + } + + VoltageLevel getVl() { + return vl; + } + + Substation getSubstation() { + return substation; + } + + public void compareSvg(Graph graph, LayoutParameters layoutParameters, String refSvgName) { + try (StringWriter writer = new StringWriter()) { + new DefaultSVGWriter(componentLibrary, layoutParameters) + .write("", graph, + new DefaultSubstationDiagramInitialValueProvider(network), + styleProvider, + new DefaultNodeLabelConfiguration(componentLibrary), + writer); + writer.flush(); + +// FileWriter fw = new FileWriter(System.getProperty("user.home") + refSvgName); +// fw.write(writer.toString()); +// fw.close(); + + String refSvg = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream(refSvgName)), StandardCharsets.UTF_8)); + String svg = normalizeLineSeparator(writer.toString()); + assertEquals(refSvg, svg); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public void compareSvg(Graph graph, LayoutParameters layoutParameters, String refSvgName, SubstationDiagramStyleProvider myStyleProvider) { + try (StringWriter writer = new StringWriter()) { + new DefaultSVGWriter(componentLibrary, layoutParameters) + .write("", graph, + new DefaultSubstationDiagramInitialValueProvider(network), + myStyleProvider, + new DefaultNodeLabelConfiguration(componentLibrary), + writer); + writer.flush(); + +// FileWriter fw = new FileWriter(System.getProperty("user.home") + refSvgName); +// fw.write(writer.toString()); +// fw.close(); + + String refSvg = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream(refSvgName)), StandardCharsets.UTF_8)); + String svg = normalizeLineSeparator(writer.toString()); + assertEquals(refSvg, svg); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public void compareSvg(SubstationGraph graph, LayoutParameters layoutParameters, String refSvgName) { + try (StringWriter writer = new StringWriter()) { + new DefaultSVGWriter(componentLibrary, layoutParameters) + .write("", graph, + new DefaultSubstationDiagramInitialValueProvider(network), + styleProvider, + new DefaultNodeLabelConfiguration(componentLibrary), + writer); + writer.flush(); + +// FileWriter fw = new FileWriter(System.getProperty("user.home") + refSvgName); +// fw.write(writer.toString()); +// fw.close(); + + String refSvg = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream(refSvgName)), StandardCharsets.UTF_8)); + String svg = normalizeLineSeparator(writer.toString()); + assertEquals(refSvg, svg); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public void compareSvg(SubstationGraph graph, LayoutParameters layoutParameters, + String refSvgName, SubstationDiagramStyleProvider myStyleProvider) { + try (StringWriter writer = new StringWriter()) { + new DefaultSVGWriter(componentLibrary, layoutParameters) + .write("", graph, + new DefaultSubstationDiagramInitialValueProvider(network), + myStyleProvider, + new DefaultNodeLabelConfiguration(componentLibrary), + writer); + writer.flush(); + +// FileWriter fw = new FileWriter(System.getProperty("user.home") + refSvgName); +// fw.write(writer.toString()); +// fw.close(); + + String refSvg = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream(refSvgName)), StandardCharsets.UTF_8)); + String svg = normalizeLineSeparator(writer.toString()); + assertEquals(refSvg, svg); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1.java new file mode 100644 index 000000000..987502540 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1.java @@ -0,0 +1,230 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayout; +import com.powsybl.sld.model.*; +import org.junit.Before; +import org.junit.Test; + +import static com.powsybl.sld.library.ComponentTypeName.*; +import static org.junit.Assert.*; + +/** + *
+ * l
+ * |
+ * b
+ * |
+ * d
+ * |
+ * ------ bbs
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase1 extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + BusbarSection bbs = view.newBusbarSection() + .setId("bbs") + .setNode(0) + .add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + Load l = vl.newLoad() + .setId("l") + .setNode(2) + .setP0(10) + .setQ0(10) + .add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, new ConnectablePosition + .Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newDisconnector() + .setId("d") + .setNode1(0) + .setNode2(1) + .add(); + view.newBreaker() + .setId("b") + .setNode1(1) + .setNode2(2) + .add(); + } + + @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + + // assert graph structure + assertEquals(5, g.getNodes().size()); + + assertEquals(Node.NodeType.BUS, g.getNodes().get(0).getType()); + assertEquals(Node.NodeType.FEEDER, g.getNodes().get(1).getType()); + assertEquals(Node.NodeType.FICTITIOUS, g.getNodes().get(2).getType()); + assertEquals(Node.NodeType.SWITCH, g.getNodes().get(3).getType()); + assertEquals(Node.NodeType.SWITCH, g.getNodes().get(4).getType()); + + assertEquals("bbs", g.getNodes().get(0).getId()); + assertEquals("l", g.getNodes().get(1).getId()); + assertEquals("FICT_vl_1", g.getNodes().get(2).getId()); + assertEquals("d", g.getNodes().get(3).getId()); + assertEquals("b", g.getNodes().get(4).getId()); + + assertEquals(BUSBAR_SECTION, g.getNodes().get(0).getComponentType()); + assertEquals(LOAD, g.getNodes().get(1).getComponentType()); + assertEquals(NODE, g.getNodes().get(2).getComponentType()); + assertEquals(DISCONNECTOR, g.getNodes().get(3).getComponentType()); + assertEquals(BREAKER, g.getNodes().get(4).getComponentType()); + + assertEquals(1, g.getNodes().get(0).getAdjacentNodes().size()); + assertEquals(1, g.getNodes().get(1).getAdjacentNodes().size()); + assertEquals(2, g.getNodes().get(2).getAdjacentNodes().size()); + assertEquals(2, g.getNodes().get(3).getAdjacentNodes().size()); + assertEquals(2, g.getNodes().get(4).getAdjacentNodes().size()); + + assertFalse(g.getNodes().get(0).isRotated()); + assertFalse(g.getNodes().get(1).isRotated()); + assertFalse(g.getNodes().get(2).isRotated()); + assertFalse(g.getNodes().get(3).isRotated()); + assertFalse(g.getNodes().get(4).isRotated()); + + assertNull(g.getNodes().get(0).getCell()); + assertNull(g.getNodes().get(1).getCell()); + assertNull(g.getNodes().get(2).getCell()); + assertNull(g.getNodes().get(3).getCell()); + assertNull(g.getNodes().get(4).getCell()); + + assertEquals(-1, g.getNodes().get(0).getX(), 0); + assertEquals(-1, g.getNodes().get(0).getY(), 0); + assertEquals(-1, g.getNodes().get(1).getX(), 0); + assertEquals(-1, g.getNodes().get(1).getY(), 0); + assertEquals(-1, g.getNodes().get(2).getX(), 0); + assertEquals(-1, g.getNodes().get(2).getY(), 0); + assertEquals(-1, g.getNodes().get(3).getX(), 0); + assertEquals(-1, g.getNodes().get(3).getY(), 0); + assertEquals(-1, g.getNodes().get(4).getX(), 0); + assertEquals(-1, g.getNodes().get(4).getY(), 0); + + assertEquals(4, g.getEdges().size()); + assertEquals("bbs", g.getEdges().get(0).getNode1().getId()); + assertEquals("d", g.getEdges().get(0).getNode2().getId()); + assertEquals("d", g.getEdges().get(1).getNode1().getId()); + assertEquals("FICT_vl_1", g.getEdges().get(1).getNode2().getId()); + assertEquals("FICT_vl_1", g.getEdges().get(2).getNode1().getId()); + assertEquals("b", g.getEdges().get(2).getNode2().getId()); + assertEquals("b", g.getEdges().get(3).getNode1().getId()); + assertEquals("l", g.getEdges().get(3).getNode2().getId()); + + assertTrue(g.getCells().isEmpty()); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + // assert cells + assertEquals(1, g.getCells().size()); + ExternCell externCell = (ExternCell) g.getCells().iterator().next(); + assertEquals(Cell.CellType.EXTERN, externCell.getType()); + assertEquals(-1, externCell.getOrder()); + assertEquals(5, externCell.getNodes().size()); + assertTrue(externCell.getPrimaryLegBlocks().isEmpty()); + assertEquals(1, externCell.getBusNodes().size()); + assertEquals("bbs", externCell.getBusNodes().get(0).getId()); + assertNull(externCell.getRootBlock()); +// assertTrue(externCell.getAbstractCellBridgingWith().isEmpty()); //TODO + assertEquals("EXTERN[FICT_vl_dFictif, b, bbs, d, l]", externCell.getFullId()); + assertEquals(new Position(0, 0), externCell.getMaxBusPosition()); + + new BlockOrganizer().organize(g); + + // assert blocks and nodes rotation + assertNotNull(externCell.getRootBlock()); + assertTrue(externCell.getRootBlock() instanceof SerialBlock); + SerialBlock bc = (SerialBlock) externCell.getRootBlock(); + assertEquals(new Position(0, 0, 1, 2, false, Orientation.VERTICAL), bc.getPosition()); + assertEquals("bbs", bc.getStartingNode().getId()); + assertEquals("l", bc.getEndingNode().getId()); + assertEquals(1, externCell.getPrimaryLegBlocks().size()); + + assertTrue(bc.getUpperBlock() instanceof BodyPrimaryBlock); + BodyPrimaryBlock ub = (BodyPrimaryBlock) bc.getUpperBlock(); + assertEquals(new Position(0, 0, 1, 2, false, Orientation.VERTICAL), ub.getPosition()); + assertEquals("FICT_vl_dFictif", ub.getStartingNode().getId()); + assertEquals("l", ub.getEndingNode().getId()); + assertTrue(ub.getStackableBlocks().isEmpty()); + + assertTrue(bc.getLowerBlock() instanceof LegPrimaryBlock); + LegPrimaryBlock lb = (LegPrimaryBlock) bc.getLowerBlock(); + assertEquals(new Position(0, 0, 1, 0, false, Orientation.VERTICAL), lb.getPosition()); + assertEquals("bbs", lb.getStartingNode().getId()); + assertEquals("FICT_vl_dFictif", lb.getEndingNode().getId()); + assertTrue(lb.getStackableBlocks().isEmpty()); + + assertFalse(g.getNodes().get(0).isRotated()); + assertFalse(g.getNodes().get(1).isRotated()); + assertFalse(g.getNodes().get(2).isRotated()); + assertFalse(g.getNodes().get(3).isRotated()); + assertFalse(g.getNodes().get(4).isRotated()); + + // calculate coordinates + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50) + .setArrowDistance(20); + + new PositionVoltageLevelLayout(g).run(layoutParameters); + + // assert coordinate + assertEquals(10, g.getNodes().get(0).getX(), 0); + assertEquals(260, g.getNodes().get(0).getY(), 0); + assertEquals(25, g.getNodes().get(1).getX(), 0); + assertEquals(-20, g.getNodes().get(1).getY(), 0); + assertEquals(25, g.getNodes().get(2).getX(), 0); + assertEquals(260, g.getNodes().get(2).getY(), 0); + assertEquals(25, g.getNodes().get(3).getX(), 0); + assertEquals(105, g.getNodes().get(3).getY(), 0); + assertEquals(25, g.getNodes().get(4).getX(), 0); + assertEquals(230, g.getNodes().get(4).getY(), 0); + + // write SVG and compare to reference + compareSvg(g, layoutParameters, "/TestCase1.svg"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase10TestBreakerToBus.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase10TestBreakerToBus.java new file mode 100644 index 000000000..42b1edf08 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase10TestBreakerToBus.java @@ -0,0 +1,63 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; + +/** + *
+ * l
+ * |
+ * b
+ * |
+ * d
+ * |
+ * ------ bbs
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase10TestBreakerToBus extends AbstractTestCase { + + @Override + void setUp() { + network = Network.create("testCase1", "AbstractTest"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + BusbarSection bbs = view.newBusbarSection() + .setId("bbs") + .setNode(0) + .add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + Load l = vl.newLoad() + .setId("l") + .setNode(2) + .setP0(10) + .setQ0(10) + .add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, new ConnectablePosition + .Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newBreaker() + .setId("b") + .setNode1(0) + .setNode2(2) + .add(); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase11SubstationGraph.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase11SubstationGraph.java new file mode 100644 index 000000000..f696680bb --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase11SubstationGraph.java @@ -0,0 +1,476 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.google.common.io.ByteStreams; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.HorizontalSubstationLayoutFactory; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayoutFactory; +import com.powsybl.sld.layout.VerticalSubstationLayoutFactory; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import com.powsybl.sld.model.SubstationGraph; +import com.powsybl.sld.svg.DefaultSVGWriter; +import com.powsybl.sld.svg.DefaultSubstationDiagramStyleProvider; +import com.powsybl.sld.util.NominalVoltageSubstationDiagramStyleProvider; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Franck Lecuyer + */ +public class TestCase11SubstationGraph extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("testCase11", "test"); + + substation = createSubstation(network, "subst", "subst", Country.FR); + + // first voltage level + // + VoltageLevel vl1 = createVoltageLevel(substation, "vl1", "vl1", TopologyKind.NODE_BREAKER, 400, 50); + + createBusBarSection(vl1, "bbs1", "bbs1", 0, 1, 1); + createBusBarSection(vl1, "bbs2", "bbs2", 1, 1, 2); + createBusBarSection(vl1, "bbs3", "bbs3", 2, 2, 1); + createBusBarSection(vl1, "bbs4", "bbs4", 3, 2, 2); + + createSwitch(vl1, "dsect11", "dsect11", SwitchKind.DISCONNECTOR, false, false, true, 0, 14); + createSwitch(vl1, "dtrct11", "dtrct11", SwitchKind.BREAKER, true, false, true, 14, 15); + createSwitch(vl1, "dsect12", "dsect12", SwitchKind.DISCONNECTOR, false, false, true, 15, 1); + + createSwitch(vl1, "dsect21", "dsect21", SwitchKind.DISCONNECTOR, false, false, true, 2, 16); + createSwitch(vl1, "dtrct21", "dtrct21", SwitchKind.BREAKER, true, false, true, 16, 17); + createSwitch(vl1, "dsect22", "dsect22", SwitchKind.DISCONNECTOR, false, false, true, 17, 3); + + createLoad(vl1, "load1", "load1", "load1", 0, ConnectablePosition.Direction.TOP, 4, 10, 10); + createSwitch(vl1, "dload1", "dload1", SwitchKind.DISCONNECTOR, false, false, true, 0, 5); + createSwitch(vl1, "bload1", "bload1", SwitchKind.BREAKER, true, false, true, 4, 5); + + createGenerator(vl1, "gen1", "gen1", "gen1", 1, ConnectablePosition.Direction.BOTTOM, 6, 0, 20, false, 10, 10); + createSwitch(vl1, "dgen1", "dgen1", SwitchKind.DISCONNECTOR, false, false, true, 2, 7); + createSwitch(vl1, "bgen1", "bgen1", SwitchKind.BREAKER, true, false, true, 6, 7); + + createLoad(vl1, "load2", "load2", "load2", 2, ConnectablePosition.Direction.TOP, 8, 10, 10); + createSwitch(vl1, "dload2", "dload2", SwitchKind.DISCONNECTOR, false, false, true, 1, 9); + createSwitch(vl1, "bload2", "bload2", SwitchKind.BREAKER, true, false, true, 8, 9); + + createGenerator(vl1, "gen2", "gen2", "gen2", 3, ConnectablePosition.Direction.BOTTOM, 10, 0, 20, false, 10, 10); + createSwitch(vl1, "dgen2", "dgen2", SwitchKind.DISCONNECTOR, false, false, true, 3, 11); + createSwitch(vl1, "bgen2", "bgen2", SwitchKind.BREAKER, true, false, true, 10, 11); + + // second voltage level + // + VoltageLevel vl2 = createVoltageLevel(substation, "vl2", "vl2", TopologyKind.NODE_BREAKER, 225, 30); + + createBusBarSection(vl2, "bbs5", "bbs5", 0, 1, 1); + createBusBarSection(vl2, "bbs6", "bbs6", 1, 2, 1); + + createSwitch(vl2, "dscpl1", "dscpl1", SwitchKind.DISCONNECTOR, false, false, true, 0, 6); + createSwitch(vl2, "ddcpl1", "ddcpl1", SwitchKind.BREAKER, true, false, true, 6, 7); + createSwitch(vl2, "dscpl2", "dscpl2", SwitchKind.DISCONNECTOR, false, false, true, 7, 1); + + createLoad(vl2, "load3", "load3", "load3", 0, ConnectablePosition.Direction.TOP, 2, 10, 10); + createSwitch(vl2, "dload3", "dload3", SwitchKind.DISCONNECTOR, false, false, true, 0, 3); + createSwitch(vl2, "bload3", "bload3", SwitchKind.BREAKER, true, false, true, 2, 3); + + createGenerator(vl2, "gen4", "gen4", "gen4", 1, ConnectablePosition.Direction.BOTTOM, 4, 0, 20, false, 10, 10); + createSwitch(vl2, "dgen4", "dgen4", SwitchKind.DISCONNECTOR, false, false, true, 1, 5); + createSwitch(vl2, "bgen4", "bgen4", SwitchKind.BREAKER, true, false, true, 4, 5); + + // third voltage level + // + VoltageLevel vl3 = createVoltageLevel(substation, "vl3", "vl3", TopologyKind.NODE_BREAKER, 225, 20); + + createBusBarSection(vl3, "bbs7", "bbs7", 0, 1, 1); + + createLoad(vl3, "load4", "load4", "load4", 0, ConnectablePosition.Direction.TOP, 1, 10, 10); + createSwitch(vl3, "dload4", "dload4", SwitchKind.DISCONNECTOR, false, false, true, 0, 2); + createSwitch(vl3, "bload4", "bload4", SwitchKind.BREAKER, true, false, true, 2, 1); + + // two windings transformers between voltage levels + // + createSwitch(vl1, "dtrf11", "dtrf11", SwitchKind.DISCONNECTOR, false, false, true, 0, 18); + createSwitch(vl1, "btrf11", "btrf11", SwitchKind.BREAKER, true, false, true, 18, 19); + createSwitch(vl2, "dtrf21", "dtrf21", SwitchKind.DISCONNECTOR, false, false, true, 0, 8); + createSwitch(vl2, "btrf21", "btrf21", SwitchKind.BREAKER, true, false, true, 8, 9); + createTwoWindingsTransformer(substation, "trf1", "trf1", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 19, 9, vl1.getId(), vl2.getId(), + "trf1", 1, ConnectablePosition.Direction.TOP, + "trf1", 1, ConnectablePosition.Direction.TOP); + + createSwitch(vl1, "dtrf12", "dtrf12", SwitchKind.DISCONNECTOR, false, false, true, 1, 20); + createSwitch(vl1, "btrf12", "btrf12", SwitchKind.BREAKER, true, false, true, 20, 21); + createSwitch(vl2, "dtrf22", "dtrf22", SwitchKind.DISCONNECTOR, false, false, true, 1, 10); + createSwitch(vl2, "btrf22", "btrf22", SwitchKind.BREAKER, true, false, true, 10, 11); + createTwoWindingsTransformer(substation, "trf2", "trf2", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 21, 11, vl1.getId(), vl2.getId(), + "trf2", 3, ConnectablePosition.Direction.TOP, + "trf2", 3, ConnectablePosition.Direction.BOTTOM); + + createSwitch(vl1, "dtrf13", "dtrf13", SwitchKind.DISCONNECTOR, false, false, true, 2, 22); + createSwitch(vl1, "btrf13", "btrf13", SwitchKind.BREAKER, true, false, true, 22, 23); + createSwitch(vl2, "dtrf23", "dtrf23", SwitchKind.DISCONNECTOR, false, false, true, 1, 12); + createSwitch(vl2, "btrf23", "btrf23", SwitchKind.BREAKER, true, false, true, 12, 13); + createTwoWindingsTransformer(substation, "trf3", "trf3", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 23, 13, vl1.getId(), vl2.getId(), + "trf3", 1, ConnectablePosition.Direction.BOTTOM, + "trf3", 4, ConnectablePosition.Direction.BOTTOM); + + createSwitch(vl1, "dtrf14", "dtrf14", SwitchKind.DISCONNECTOR, false, false, true, 3, 24); + createSwitch(vl1, "btrf14", "btrf14", SwitchKind.BREAKER, true, false, true, 24, 25); + createSwitch(vl2, "dtrf24", "dtrf24", SwitchKind.DISCONNECTOR, false, false, true, 0, 14); + createSwitch(vl2, "btrf24", "btrf24", SwitchKind.BREAKER, true, false, true, 14, 15); + createTwoWindingsTransformer(substation, "trf4", "trf4", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 25, 15, vl1.getId(), vl2.getId(), + "trf4", 2, ConnectablePosition.Direction.BOTTOM, + "trf4", 2, ConnectablePosition.Direction.TOP); + + createSwitch(vl1, "dtrf15", "dtrf15", SwitchKind.DISCONNECTOR, false, false, true, 0, 26); + createSwitch(vl1, "btrf15", "btrf15", SwitchKind.BREAKER, true, false, true, 26, 27); + createSwitch(vl3, "dtrf25", "dtrf25", SwitchKind.DISCONNECTOR, false, false, true, 0, 3); + createSwitch(vl3, "btrf25", "btrf25", SwitchKind.BREAKER, true, false, true, 3, 4); + createTwoWindingsTransformer(substation, "trf5", "trf5", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 27, 4, vl1.getId(), vl3.getId(), + "trf5", 2, ConnectablePosition.Direction.TOP, + "trf5", 1, ConnectablePosition.Direction.BOTTOM); + + // three windings transformers between voltage levels + // + createSwitch(vl1, "dtrf16", "dtrf16", SwitchKind.DISCONNECTOR, false, false, true, 0, 28); + createSwitch(vl1, "btrf16", "btrf16", SwitchKind.BREAKER, true, false, true, 28, 29); + createSwitch(vl2, "dtrf26", "dtrf26", SwitchKind.DISCONNECTOR, false, false, true, 1, 16); + createSwitch(vl2, "btrf26", "btrf26", SwitchKind.BREAKER, true, false, true, 16, 17); + createSwitch(vl3, "dtrf36", "dtrf36", SwitchKind.DISCONNECTOR, false, false, true, 0, 5); + createSwitch(vl3, "btrf36", "btrf36", SwitchKind.BREAKER, true, false, true, 5, 6); + + createThreeWindingsTransformer(substation, "trf6", "trf6", vl1.getId(), vl2.getId(), vl3.getId(), + 0.5, 0.5, 0.5, 1., 1., 1., 0.1, 0.1, + 400., 225., 225., + 29, 17, 6, + "trf61", 2, ConnectablePosition.Direction.TOP, + "trf62", 2, ConnectablePosition.Direction.TOP, + "trf63", 2, ConnectablePosition.Direction.TOP); + + createSwitch(vl1, "dtrf17", "dtrf17", SwitchKind.DISCONNECTOR, false, false, true, 2, 30); + createSwitch(vl1, "btrf17", "btrf17", SwitchKind.BREAKER, true, false, true, 30, 31); + createSwitch(vl2, "dtrf27", "dtrf27", SwitchKind.DISCONNECTOR, false, false, true, 0, 18); + createSwitch(vl2, "btrf27", "btrf27", SwitchKind.BREAKER, true, false, true, 18, 19); + createSwitch(vl3, "dtrf37", "dtrf37", SwitchKind.DISCONNECTOR, false, false, true, 0, 7); + createSwitch(vl3, "btrf37", "btrf37", SwitchKind.BREAKER, true, false, true, 7, 8); + + createThreeWindingsTransformer(substation, "trf7", "trf7", vl1.getId(), vl2.getId(), vl3.getId(), + 0.5, 0.5, 0.5, 1., 1., 1., 0.1, 0.1, + 400., 225., 225., + 31, 19, 8, + "trf71", 2, ConnectablePosition.Direction.BOTTOM, + "trf72", 2, ConnectablePosition.Direction.TOP, + "trf73", 2, ConnectablePosition.Direction.BOTTOM); + + createSwitch(vl1, "dtrf18", "dtrf18", SwitchKind.DISCONNECTOR, false, false, true, 1, 32); + createSwitch(vl1, "btrf18", "btrf18", SwitchKind.BREAKER, true, false, true, 32, 33); + createSwitch(vl2, "dtrf28", "dtrf28", SwitchKind.DISCONNECTOR, false, false, true, 1, 20); + createSwitch(vl2, "btrf28", "btrf28", SwitchKind.BREAKER, true, false, true, 20, 21); + createSwitch(vl3, "dtrf38", "dtrf38", SwitchKind.DISCONNECTOR, false, false, true, 0, 9); + createSwitch(vl3, "btrf38", "btrf38", SwitchKind.BREAKER, true, false, true, 9, 10); + + createThreeWindingsTransformer(substation, "trf8", "trf8", vl1.getId(), vl2.getId(), vl3.getId(), + 0.5, 0.5, 0.5, 1., 1., 1., 0.1, 0.1, + 400., 225., 225., + 33, 21, 10, + "trf81", 2, ConnectablePosition.Direction.TOP, + "trf82", 2, ConnectablePosition.Direction.BOTTOM, + "trf83", 2, ConnectablePosition.Direction.TOP); + + // Creation of another substation, another voltageLevel and a line between the two substations + // + Substation substation2 = createSubstation(network, "subst2", "subst2", Country.FR); + VoltageLevel vlSubst2 = createVoltageLevel(substation2, "vlSubst2", "vlSubst2", TopologyKind.NODE_BREAKER, 400, 50); + + createBusBarSection(vlSubst2, "bbs1_2", "bbs1_2", 0, 1, 1); + + createSwitch(vl1, "dline11_2", "dline11_2", SwitchKind.DISCONNECTOR, false, false, true, 0, 34); + createSwitch(vl1, "bline11_2", "bline11_2", SwitchKind.BREAKER, true, false, true, 34, 35); + createSwitch(vlSubst2, "dline21_2", "dline21_2", SwitchKind.DISCONNECTOR, false, false, true, 0, 1); + createSwitch(vlSubst2, "bline21_2", "bline21_2", SwitchKind.BREAKER, true, false, true, 1, 2); + createLine(network, "line1", "line1", 2.0, 14.745, 1.0, 1.0, 1.0, 1.0, + 35, 2, vl1.getId(), vlSubst2.getId(), + "line1", 3, ConnectablePosition.Direction.TOP, + "line1", 1, ConnectablePosition.Direction.TOP); + } + + private static Substation createSubstation(Network n, String id, String name, Country country) { + Substation s = n.newSubstation() + .setId(id) + .setName(name) + .setCountry(country) + .add(); + return s; + } + + private static VoltageLevel createVoltageLevel(Substation s, String id, String name, + TopologyKind topology, double vNom, int nodeCount) { + VoltageLevel vl = s.newVoltageLevel() + .setId(id) + .setName(name) + .setTopologyKind(topology) + .setNominalV(vNom) + .add(); + vl.getNodeBreakerView() + .setNodeCount(nodeCount); + return vl; + } + + private static void createSwitch(VoltageLevel vl, String id, String name, SwitchKind kind, boolean retained, boolean open, boolean fictitious, int node1, int node2) { + vl.getNodeBreakerView().newSwitch() + .setId(id) + .setName(name) + .setKind(kind) + .setRetained(retained) + .setOpen(open) + .setFictitious(fictitious) + .setNode1(node1) + .setNode2(node2) + .add(); + } + + private static void createBusBarSection(VoltageLevel vl, String id, String name, int node, int busbarIndex, int sectionIndex) { + BusbarSection bbs = vl.getNodeBreakerView().newBusbarSection() + .setId(id) + .setName(name) + .setNode(node) + .add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, busbarIndex, sectionIndex)); + } + + private static void createLoad(VoltageLevel vl, String id, String name, String feederName, int feederOrder, + ConnectablePosition.Direction direction, int node, double p0, double q0) { + Load load = vl.newLoad() + .setId(id) + .setName(name) + .setNode(node) + .setP0(p0) + .setQ0(q0) + .add(); + load.addExtension(ConnectablePosition.class, new ConnectablePosition<>(load, new ConnectablePosition + .Feeder(feederName, feederOrder, direction), null, null, null)); + } + + private static void createGenerator(VoltageLevel vl, String id, String name, String feederName, int feederOrder, + ConnectablePosition.Direction direction, int node, + double minP, double maxP, boolean voltageRegulator, + double targetP, double targetQ) { + Generator gen = vl.newGenerator() + .setId(id) + .setName(name) + .setNode(node) + .setMinP(minP) + .setMaxP(maxP) + .setVoltageRegulatorOn(voltageRegulator) + .setTargetP(targetP) + .setTargetQ(targetQ) + .add(); + gen.addExtension(ConnectablePosition.class, new ConnectablePosition<>(gen, new ConnectablePosition + .Feeder(feederName, feederOrder, direction), null, null, null)); + } + + private static void createTwoWindingsTransformer(Substation s, String id, String name, + double r, double x, double g, double b, + double ratedU1, double ratedU2, + int node1, int node2, + String idVoltageLevel1, String idVoltageLevel2, + String feederName1, int feederOrder1, ConnectablePosition.Direction direction1, + String feederName2, int feederOrder2, ConnectablePosition.Direction direction2) { + TwoWindingsTransformer t = s.newTwoWindingsTransformer() + .setId(id) + .setName(name) + .setR(r) + .setX(x) + .setG(g) + .setB(b) + .setRatedU1(ratedU1) + .setRatedU2(ratedU2) + .setNode1(node1) + .setVoltageLevel1(idVoltageLevel1) + .setNode2(node2) + .setVoltageLevel2(idVoltageLevel2) + .add(); + t.addExtension(ConnectablePosition.class, + new ConnectablePosition<>(t, + null, + new ConnectablePosition.Feeder(feederName1, feederOrder1, direction1), + new ConnectablePosition.Feeder(feederName2, feederOrder2, direction2), + null)); + } + + private static void createThreeWindingsTransformer(Substation s, String id, String name, + String vl1, String vl2, String vl3, + double r1, double r2, double r3, + double x1, double x2, double x3, + double g1, double b1, + double ratedU1, double ratedU2, double ratedU3, + int node1, int node2, int node3, + String feederName1, int feederOrder1, ConnectablePosition.Direction direction1, + String feederName2, int feederOrder2, ConnectablePosition.Direction direction2, + String feederName3, int feederOrder3, ConnectablePosition.Direction direction3) { + ThreeWindingsTransformer t = s.newThreeWindingsTransformer() + .setId(id) + .setName(name) + .newLeg1() + .setR(r1) + .setX(x1) + .setG(g1) + .setB(b1) + .setRatedU(ratedU1) + .setVoltageLevel(vl1) + .setNode(node1) + .add() + .newLeg2() + .setR(r2) + .setX(x2) + .setRatedU(ratedU2) + .setVoltageLevel(vl2) + .setNode(node2) + .add() + .newLeg3() + .setR(r3) + .setX(x3) + .setRatedU(ratedU3) + .setVoltageLevel(vl3) + .setNode(node3) + .add() + .add(); + + t.addExtension(ConnectablePosition.class, + new ConnectablePosition<>(t, + null, + new ConnectablePosition.Feeder(feederName1, feederOrder1, direction1), + new ConnectablePosition.Feeder(feederName2, feederOrder2, direction2), + new ConnectablePosition.Feeder(feederName3, feederOrder3, direction3))); + } + + private static void createLine(Network network, + String id, String name, + double r, double x, + double g1, double b1, + double g2, double b2, + int node1, int node2, + String idVoltageLevel1, String idVoltageLevel2, + String feederName1, int feederOrder1, ConnectablePosition.Direction direction1, + String feederName2, int feederOrder2, ConnectablePosition.Direction direction2) { + Line line = network.newLine() + .setId(id) + .setName(name) + .setR(r) + .setX(x) + .setG1(g1) + .setB1(b1) + .setG2(g2) + .setB2(b2) + .setNode1(node1) + .setVoltageLevel1(idVoltageLevel1) + .setNode2(node2) + .setVoltageLevel2(idVoltageLevel2) + .add(); + line.addExtension(ConnectablePosition.class, + new ConnectablePosition<>(line, + null, + new ConnectablePosition.Feeder(feederName1, feederOrder1, direction1), + new ConnectablePosition.Feeder(feederName2, feederOrder2, direction2), + null)); + } + + @Test + public void test() { + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(false) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50) + .setDrawStraightWires(false) + .setHorizontalSnakeLinePadding(30) + .setVerticalSnakeLinePadding(30) + .setShowInductorFor3WT(false); + + // build substation graph + SubstationGraph g = SubstationGraph.create(substation); + + // assert substation graph structure + assertEquals(3, g.getNodes().size()); + assertEquals(14, g.getEdges().size()); + + // write SVG and compare to reference (horizontal layout and defaut style provider) + new HorizontalSubstationLayoutFactory().create(g, new PositionVoltageLevelLayoutFactory()).run(layoutParameters); + compareSvg(g, layoutParameters, "/TestCase11SubstationGraphHorizontal.svg", + new DefaultSubstationDiagramStyleProvider()); + + // rebuild substation graph + g = SubstationGraph.create(substation); + + // write SVG and compare to reference (vertical layout) + new VerticalSubstationLayoutFactory().create(g, new PositionVoltageLevelLayoutFactory()).run(layoutParameters); + compareSvg(g, layoutParameters, "/TestCase11SubstationGraphVertical.svg"); + + // rebuild substation graph + g = SubstationGraph.create(substation); + + // write SVG and compare to reference (horizontal layout and nominal voltage style) + new HorizontalSubstationLayoutFactory().create(g, new PositionVoltageLevelLayoutFactory()).run(layoutParameters); + compareSvg(g, layoutParameters, "/TestCase11SubstationGraphHorizontalNominalVoltageLevel.svg", + new NominalVoltageSubstationDiagramStyleProvider()); + + // Create substation diagram (svg + metadata files) + SubstationDiagram diagram = SubstationDiagram.build(substation); + Path pathSVG = Paths.get(System.getProperty("user.home"), "substDiag.svg"); + Path pathMetadata = Paths.get(System.getProperty("user.home"), "substDiag_metadata.json"); + diagram.writeSvg("", new DefaultSVGWriter(new ResourcesComponentLibrary("/ConvergenceLibrary"), layoutParameters), pathSVG, network); + assertTrue(Files.exists(pathSVG)); + assertTrue(Files.exists(pathMetadata)); + try { + String refSvg = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/substDiag.svg")), StandardCharsets.UTF_8)); + String svg = normalizeLineSeparator(new String(Files.readAllBytes(pathSVG), StandardCharsets.UTF_8)); + assertEquals(refSvg, svg); + Files.deleteIfExists(pathSVG); + + String refMetadata = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/substDiag_metadata.json")), StandardCharsets.UTF_8)); + String metadata = normalizeLineSeparator(new String(Files.readAllBytes(pathMetadata), StandardCharsets.UTF_8)); + assertEquals(refMetadata, metadata); + Files.deleteIfExists(pathMetadata); + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + LayoutParameters lp2 = new LayoutParameters(layoutParameters); + assertEquals(lp2.getCellWidth(), layoutParameters.getCellWidth(), 0.); + assertEquals(lp2.getHorizontalSnakeLinePadding(), layoutParameters.getHorizontalSnakeLinePadding(), 0.); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase12GraphWith3WT.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase12GraphWith3WT.java new file mode 100644 index 000000000..a94b8b1e4 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase12GraphWith3WT.java @@ -0,0 +1,465 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.google.common.io.ByteStreams; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.*; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.svg.DefaultNodeLabelConfiguration; +import com.powsybl.sld.svg.DefaultSubstationDiagramInitialValueProvider; +import com.powsybl.sld.svg.DefaultSVGWriter; +import com.powsybl.sld.util.NominalVoltageSubstationDiagramStyleProvider; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.Assert.assertEquals; + +/** + * @author Franck Lecuyer + */ +public class TestCase12GraphWith3WT extends AbstractTestCase { + + private VoltageLevel vl1; + private VoltageLevel vl2; + private VoltageLevel vl3; + + @Before + public void setUp() { + network = Network.create("testCase11", "test"); + + substation = createSubstation(network, "subst", "subst", Country.FR); + + // first voltage level + // + vl1 = createVoltageLevel(substation, "vl1", "vl1", TopologyKind.NODE_BREAKER, 400, 50); + + createBusBarSection(vl1, "bbs1", "bbs1", 0, 1, 1); + createBusBarSection(vl1, "bbs2", "bbs2", 1, 1, 2); + createBusBarSection(vl1, "bbs3", "bbs3", 2, 2, 1); + createBusBarSection(vl1, "bbs4", "bbs4", 3, 2, 2); + + createSwitch(vl1, "dsect11", "dsect11", SwitchKind.DISCONNECTOR, false, false, true, 0, 14); + createSwitch(vl1, "dtrct11", "dtrct11", SwitchKind.BREAKER, true, false, true, 14, 15); + createSwitch(vl1, "dsect12", "dsect12", SwitchKind.DISCONNECTOR, false, false, true, 15, 1); + + createSwitch(vl1, "dsect21", "dsect21", SwitchKind.DISCONNECTOR, false, false, true, 2, 16); + createSwitch(vl1, "dtrct21", "dtrct21", SwitchKind.BREAKER, true, false, true, 16, 17); + createSwitch(vl1, "dsect22", "dsect22", SwitchKind.DISCONNECTOR, false, false, true, 17, 3); + + createLoad(vl1, "load1", "load1", "load1", 0, ConnectablePosition.Direction.TOP, 4, 10, 10); + createSwitch(vl1, "dload1", "dload1", SwitchKind.DISCONNECTOR, false, false, true, 0, 5); + createSwitch(vl1, "bload1", "bload1", SwitchKind.BREAKER, true, false, true, 4, 5); + + createGenerator(vl1, "gen1", "gen1", "gen1", 1, ConnectablePosition.Direction.BOTTOM, 6, 0, 20, false, 10, 10); + createSwitch(vl1, "dgen1", "dgen1", SwitchKind.DISCONNECTOR, false, false, true, 2, 7); + createSwitch(vl1, "bgen1", "bgen1", SwitchKind.BREAKER, true, false, true, 6, 7); + + createLoad(vl1, "load2", "load2", "load2", 2, ConnectablePosition.Direction.TOP, 8, 10, 10); + createSwitch(vl1, "dload2", "dload2", SwitchKind.DISCONNECTOR, false, false, true, 1, 9); + createSwitch(vl1, "bload2", "bload2", SwitchKind.BREAKER, true, false, true, 8, 9); + + createGenerator(vl1, "gen2", "gen2", "gen2", 3, ConnectablePosition.Direction.BOTTOM, 10, 0, 20, false, 10, 10); + createSwitch(vl1, "dgen2", "dgen2", SwitchKind.DISCONNECTOR, false, false, true, 3, 11); + createSwitch(vl1, "bgen2", "bgen2", SwitchKind.BREAKER, true, false, true, 10, 11); + + // second voltage level + // + vl2 = createVoltageLevel(substation, "vl2", "vl2", TopologyKind.NODE_BREAKER, 225, 50); + + createBusBarSection(vl2, "bbs5", "bbs5", 0, 1, 1); + createBusBarSection(vl2, "bbs6", "bbs6", 1, 2, 1); + + createSwitch(vl2, "dscpl1", "dscpl1", SwitchKind.DISCONNECTOR, false, false, true, 0, 6); + createSwitch(vl2, "ddcpl1", "ddcpl1", SwitchKind.BREAKER, true, false, true, 6, 7); + createSwitch(vl2, "dscpl2", "dscpl2", SwitchKind.DISCONNECTOR, false, false, true, 7, 1); + + createLoad(vl2, "load3", "load3", "load3", 0, ConnectablePosition.Direction.TOP, 2, 10, 10); + createSwitch(vl2, "dload3", "dload3", SwitchKind.DISCONNECTOR, false, false, true, 0, 3); + createSwitch(vl2, "bload3", "bload3", SwitchKind.BREAKER, true, false, true, 2, 3); + + createGenerator(vl2, "gen4", "gen4", "gen4", 1, ConnectablePosition.Direction.BOTTOM, 4, 0, 20, false, 10, 10); + createSwitch(vl2, "dgen4", "dgen4", SwitchKind.DISCONNECTOR, false, false, true, 1, 5); + createSwitch(vl2, "bgen4", "bgen4", SwitchKind.BREAKER, true, false, true, 4, 5); + + // third voltage level + // + vl3 = createVoltageLevel(substation, "vl3", "vl3", TopologyKind.NODE_BREAKER, 63, 50); + + createBusBarSection(vl3, "bbs7", "bbs7", 0, 1, 1); + + createLoad(vl3, "load4", "load4", "load4", 0, ConnectablePosition.Direction.TOP, 1, 10, 10); + createSwitch(vl3, "dload4", "dload4", SwitchKind.DISCONNECTOR, false, false, true, 0, 2); + createSwitch(vl3, "bload4", "bload4", SwitchKind.BREAKER, true, false, true, 2, 1); + createShunt(vl3, "self4", "self4", "self4", 1, ConnectablePosition.Direction.BOTTOM, 3, -1, 1, 1); + createSwitch(vl3, "dself4", "dself4", SwitchKind.DISCONNECTOR, false, false, true, 0, 4); + createSwitch(vl3, "bself4", "bself4", SwitchKind.BREAKER, true, false, true, 4, 3); + + // two windings transformers between voltage levels + // + createSwitch(vl1, "dtrf11", "dtrf11", SwitchKind.DISCONNECTOR, false, false, true, 0, 18); + createSwitch(vl1, "btrf11", "btrf11", SwitchKind.BREAKER, true, false, true, 18, 19); + createSwitch(vl2, "dtrf21", "dtrf21", SwitchKind.DISCONNECTOR, false, false, true, 0, 8); + createSwitch(vl2, "btrf21", "btrf21", SwitchKind.BREAKER, true, false, true, 8, 9); + createTwoWindingsTransformer(substation, "trf1", "trf1", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 19, 9, vl1.getId(), vl2.getId(), + "trf1", 1, ConnectablePosition.Direction.TOP, + "trf1", 1, ConnectablePosition.Direction.TOP); + + createSwitch(vl1, "dtrf12", "dtrf12", SwitchKind.DISCONNECTOR, false, false, true, 1, 20); + createSwitch(vl1, "btrf12", "btrf12", SwitchKind.BREAKER, true, false, true, 20, 21); + createSwitch(vl2, "dtrf22", "dtrf22", SwitchKind.DISCONNECTOR, false, false, true, 1, 10); + createSwitch(vl2, "btrf22", "btrf22", SwitchKind.BREAKER, true, false, true, 10, 11); + createTwoWindingsTransformer(substation, "trf2", "trf2", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 21, 11, vl1.getId(), vl2.getId(), + "trf2", 3, ConnectablePosition.Direction.TOP, + "trf2", 3, ConnectablePosition.Direction.BOTTOM); + + createSwitch(vl1, "dtrf13", "dtrf13", SwitchKind.DISCONNECTOR, false, false, true, 2, 22); + createSwitch(vl1, "btrf13", "btrf13", SwitchKind.BREAKER, true, false, true, 22, 23); + createSwitch(vl2, "dtrf23", "dtrf23", SwitchKind.DISCONNECTOR, false, false, true, 1, 12); + createSwitch(vl2, "btrf23", "btrf23", SwitchKind.BREAKER, true, false, true, 12, 13); + createTwoWindingsTransformer(substation, "trf3", "trf3", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 23, 13, vl1.getId(), vl2.getId(), + "trf3", 1, ConnectablePosition.Direction.BOTTOM, + "trf3", 4, ConnectablePosition.Direction.BOTTOM); + + createSwitch(vl1, "dtrf14", "dtrf14", SwitchKind.DISCONNECTOR, false, false, true, 3, 24); + createSwitch(vl1, "btrf14", "btrf14", SwitchKind.BREAKER, true, false, true, 24, 25); + createSwitch(vl2, "dtrf24", "dtrf24", SwitchKind.DISCONNECTOR, false, false, true, 0, 14); + createSwitch(vl2, "btrf24", "btrf24", SwitchKind.BREAKER, true, false, true, 14, 15); + createTwoWindingsTransformer(substation, "trf4", "trf4", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 25, 15, vl1.getId(), vl2.getId(), + "trf4", 2, ConnectablePosition.Direction.BOTTOM, + "trf4", 2, ConnectablePosition.Direction.TOP); + + createSwitch(vl1, "dtrf15", "dtrf15", SwitchKind.DISCONNECTOR, false, false, true, 0, 26); + createSwitch(vl1, "btrf15", "btrf15", SwitchKind.BREAKER, true, false, true, 26, 27); + createSwitch(vl3, "dtrf25", "dtrf25", SwitchKind.DISCONNECTOR, false, false, true, 0, 5); + createSwitch(vl3, "btrf25", "btrf25", SwitchKind.BREAKER, true, false, true, 5, 6); + createTwoWindingsTransformer(substation, "trf5", "trf5", 2.0, 14.745, 0.0, 3.2E-5, 400.0, 225.0, + 27, 6, vl1.getId(), vl3.getId(), + "trf5", 2, ConnectablePosition.Direction.TOP, + "trf5", 1, ConnectablePosition.Direction.BOTTOM); + + // three windings transformers between voltage levels + // + createSwitch(vl1, "dtrf16", "dtrf16", SwitchKind.DISCONNECTOR, false, false, true, 0, 28); + createSwitch(vl1, "btrf16", "btrf16", SwitchKind.BREAKER, true, false, true, 28, 29); + createSwitch(vl2, "dtrf26", "dtrf26", SwitchKind.DISCONNECTOR, false, false, true, 1, 16); + createSwitch(vl2, "btrf26", "btrf26", SwitchKind.BREAKER, true, false, true, 16, 17); + createSwitch(vl3, "dtrf36", "dtrf36", SwitchKind.DISCONNECTOR, false, false, true, 0, 7); + createSwitch(vl3, "btrf36", "btrf36", SwitchKind.BREAKER, true, false, true, 7, 8); + + createThreeWindingsTransformer(substation, "trf6", "trf6", vl1.getId(), vl2.getId(), vl3.getId(), + 0.5, 0.5, 0.5, 1., 1., 1., 0.1, 0.1, + 400., 225., 225., + 29, 17, 8, + "trf61", 2, ConnectablePosition.Direction.TOP, + "trf62", 2, ConnectablePosition.Direction.TOP, + "trf63", 2, ConnectablePosition.Direction.TOP); + + createSwitch(vl1, "dtrf17", "dtrf17", SwitchKind.DISCONNECTOR, false, false, true, 2, 30); + createSwitch(vl1, "btrf17", "btrf17", SwitchKind.BREAKER, true, false, true, 30, 31); + createSwitch(vl2, "dtrf27", "dtrf27", SwitchKind.DISCONNECTOR, false, false, true, 0, 18); + createSwitch(vl2, "btrf27", "btrf27", SwitchKind.BREAKER, true, false, true, 18, 19); + createSwitch(vl3, "dtrf37", "dtrf37", SwitchKind.DISCONNECTOR, false, false, true, 0, 9); + createSwitch(vl3, "btrf37", "btrf37", SwitchKind.BREAKER, true, false, true, 9, 10); + + createThreeWindingsTransformer(substation, "trf7", "trf7", vl1.getId(), vl2.getId(), vl3.getId(), + 0.5, 0.5, 0.5, 1., 1., 1., 0.1, 0.1, + 400., 225., 225., + 31, 19, 10, + "trf71", 2, ConnectablePosition.Direction.BOTTOM, + "trf72", 2, ConnectablePosition.Direction.TOP, + "trf73", 2, ConnectablePosition.Direction.BOTTOM); + + createSwitch(vl1, "dtrf18", "dtrf18", SwitchKind.DISCONNECTOR, false, false, true, 1, 32); + createSwitch(vl1, "btrf18", "btrf18", SwitchKind.BREAKER, true, false, true, 32, 33); + createSwitch(vl2, "dtrf28", "dtrf28", SwitchKind.DISCONNECTOR, false, false, true, 1, 20); + createSwitch(vl2, "btrf28", "btrf28", SwitchKind.BREAKER, true, false, true, 20, 21); + createSwitch(vl3, "dtrf38", "dtrf38", SwitchKind.DISCONNECTOR, false, false, true, 0, 11); + createSwitch(vl3, "btrf38", "btrf38", SwitchKind.BREAKER, true, false, true, 11, 12); + + createThreeWindingsTransformer(substation, "trf8", "trf8", vl1.getId(), vl2.getId(), vl3.getId(), + 0.5, 0.5, 0.5, 1., 1., 1., 0.1, 0.1, + 400., 225., 225., + 33, 21, 12, + "trf81", 2, ConnectablePosition.Direction.TOP, + "trf82", 2, ConnectablePosition.Direction.BOTTOM, + "trf83", 2, ConnectablePosition.Direction.TOP); + + createShunt(vl3, "self5", "self5", "self5", 2, ConnectablePosition.Direction.BOTTOM, 13, -1, 1, 1); + createSwitch(vl3, "dself5", "dself5", SwitchKind.DISCONNECTOR, false, false, true, 0, 14); + createSwitch(vl3, "bself5", "bself5", SwitchKind.BREAKER, true, false, true, 14, 13); + + createBusBarSection(vl3, "bbs8", "bbs8", 15, 1, 2); + + createShunt(vl3, "self6", "self6", "self6", 3, ConnectablePosition.Direction.BOTTOM, 16, 1, 1, 1); + createSwitch(vl3, "dself6", "dself6", SwitchKind.DISCONNECTOR, false, false, true, 15, 17); + createSwitch(vl3, "bself6", "bself6", SwitchKind.BREAKER, true, false, true, 17, 16); + } + + private static Substation createSubstation(Network n, String id, String name, Country country) { + Substation s = n.newSubstation() + .setId(id) + .setName(name) + .setCountry(country) + .add(); + return s; + } + + private static VoltageLevel createVoltageLevel(Substation s, String id, String name, + TopologyKind topology, double vNom, int nodeCount) { + VoltageLevel vl = s.newVoltageLevel() + .setId(id) + .setName(name) + .setTopologyKind(topology) + .setNominalV(vNom) + .add(); + vl.getNodeBreakerView() + .setNodeCount(nodeCount); + return vl; + } + + private static void createSwitch(VoltageLevel vl, String id, String name, SwitchKind kind, boolean retained, boolean open, boolean fictitious, int node1, int node2) { + vl.getNodeBreakerView().newSwitch() + .setId(id) + .setName(name) + .setKind(kind) + .setRetained(retained) + .setOpen(open) + .setFictitious(fictitious) + .setNode1(node1) + .setNode2(node2) + .add(); + } + + private static void createBusBarSection(VoltageLevel vl, String id, String name, int node, int busbarIndex, int sectionIndex) { + BusbarSection bbs = vl.getNodeBreakerView().newBusbarSection() + .setId(id) + .setName(name) + .setNode(node) + .add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, busbarIndex, sectionIndex)); + } + + private static void createLoad(VoltageLevel vl, String id, String name, String feederName, int feederOrder, + ConnectablePosition.Direction direction, int node, double p0, double q0) { + Load load = vl.newLoad() + .setId(id) + .setName(name) + .setNode(node) + .setP0(p0) + .setQ0(q0) + .add(); + load.addExtension(ConnectablePosition.class, new ConnectablePosition<>(load, new ConnectablePosition + .Feeder(feederName, feederOrder, direction), null, null, null)); + } + + private static void createGenerator(VoltageLevel vl, String id, String name, String feederName, int feederOrder, + ConnectablePosition.Direction direction, int node, + double minP, double maxP, boolean voltageRegulator, + double targetP, double targetQ) { + Generator gen = vl.newGenerator() + .setId(id) + .setName(name) + .setNode(node) + .setMinP(minP) + .setMaxP(maxP) + .setVoltageRegulatorOn(voltageRegulator) + .setTargetP(targetP) + .setTargetQ(targetQ) + .add(); + gen.addExtension(ConnectablePosition.class, new ConnectablePosition<>(gen, new ConnectablePosition + .Feeder(feederName, feederOrder, direction), null, null, null)); + } + + private static void createShunt(VoltageLevel vl, String id, String name, String feederName, int feederOrder, + ConnectablePosition.Direction direction, int node, + double bPerSection, int maximumSectionCount, int currentSectionCount) { + ShuntCompensator shunt = vl.newShuntCompensator() + .setId(id) + .setName(name) + .setNode(node) + .setbPerSection(bPerSection) + .setMaximumSectionCount(maximumSectionCount) + .setCurrentSectionCount(currentSectionCount) + .add(); + shunt.addExtension(ConnectablePosition.class, new ConnectablePosition<>(shunt, new ConnectablePosition + .Feeder(feederName, feederOrder, direction), null, null, null)); + } + + private static void createTwoWindingsTransformer(Substation s, String id, String name, + double r, double x, double g, double b, + double ratedU1, double ratedU2, + int node1, int node2, + String idVoltageLevel1, String idVoltageLevel2, + String feederName1, int feederOrder1, ConnectablePosition.Direction direction1, + String feederName2, int feederOrder2, ConnectablePosition.Direction direction2) { + TwoWindingsTransformer t = s.newTwoWindingsTransformer() + .setId(id) + .setName(name) + .setR(r) + .setX(x) + .setG(g) + .setB(b) + .setRatedU1(ratedU1) + .setRatedU2(ratedU2) + .setNode1(node1) + .setVoltageLevel1(idVoltageLevel1) + .setNode2(node2) + .setVoltageLevel2(idVoltageLevel2) + .add(); + t.addExtension(ConnectablePosition.class, + new ConnectablePosition<>(t, + null, + new ConnectablePosition.Feeder(feederName1, feederOrder1, direction1), + new ConnectablePosition.Feeder(feederName2, feederOrder2, direction2), + null)); + } + + private static void createThreeWindingsTransformer(Substation s, String id, String name, + String vl1, String vl2, String vl3, + double r1, double r2, double r3, + double x1, double x2, double x3, + double g1, double b1, + double ratedU1, double ratedU2, double ratedU3, + int node1, int node2, int node3, + String feederName1, int feederOrder1, ConnectablePosition.Direction direction1, + String feederName2, int feederOrder2, ConnectablePosition.Direction direction2, + String feederName3, int feederOrder3, ConnectablePosition.Direction direction3) { + ThreeWindingsTransformer t = s.newThreeWindingsTransformer() + .setId(id) + .setName(name) + .newLeg1() + .setR(r1) + .setX(x1) + .setG(g1) + .setB(b1) + .setRatedU(ratedU1) + .setVoltageLevel(vl1) + .setNode(node1) + .add() + .newLeg2() + .setR(r2) + .setX(x2) + .setRatedU(ratedU2) + .setVoltageLevel(vl2) + .setNode(node2) + .add() + .newLeg3() + .setR(r3) + .setX(x3) + .setRatedU(ratedU3) + .setVoltageLevel(vl3) + .setNode(node3) + .add() + .add(); + + t.addExtension(ConnectablePosition.class, + new ConnectablePosition<>(t, + null, + new ConnectablePosition.Feeder(feederName1, feederOrder1, direction1), + new ConnectablePosition.Feeder(feederName2, feederOrder2, direction2), + new ConnectablePosition.Feeder(feederName3, feederOrder3, direction3))); + } + + @Test + public void test() { + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(80) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(false) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50) + .setDrawStraightWires(false) + .setHorizontalSnakeLinePadding(30) + .setVerticalSnakeLinePadding(30) + .setShowInductorFor3WT(true); + + // build voltage level 1 graph + Graph g1 = Graph.create(vl1, false, true, true); + new ImplicitCellDetector().detectCells(g1); + new BlockOrganizer().organize(g1); + new PositionVoltageLevelLayout(g1).run(layoutParameters); + + Graph g2 = Graph.create(vl2, false, true, true); + new ImplicitCellDetector().detectCells(g2); + new BlockOrganizer().organize(g2); + new PositionVoltageLevelLayout(g2).run(layoutParameters); + + Graph g3 = Graph.create(vl3, false, true, false); + new ImplicitCellDetector().detectCells(g3); + new BlockOrganizer().organize(g3); + new PositionVoltageLevelLayout(g3).run(layoutParameters); + + // write SVG and compare to reference (horizontal layout) + compareSvg(g1, layoutParameters, "/TestCase12GraphVL1.svg"); + compareSvg(g2, layoutParameters, "/TestCase12GraphVL2.svg"); + compareSvg(g3, layoutParameters, "/TestCase12GraphVL3.svg"); + + // write optimized SVG and compare to reference (horizontal layout) + LayoutParameters layoutParametersOptimized = new LayoutParameters(layoutParameters); + layoutParametersOptimized.setAvoidSVGComponentsDuplication(true); + + compareSvg(g1, layoutParametersOptimized, "/TestCase12GraphVL1_optimized.svg"); + compareSvg(g2, layoutParametersOptimized, "/TestCase12GraphVL2_optimized.svg"); + compareSvg(g3, layoutParametersOptimized, "/TestCase12GraphVL3_optimized.svg"); + + // Create voltageLevel diagram (svg + metadata files) + VoltageLevelDiagram diagram = VoltageLevelDiagram.build(vl1, new PositionVoltageLevelLayoutFactory(), false, true); + Path pathSVG = Paths.get(System.getProperty("user.home"), "vlDiag.svg"); + Path pathMetadata = Paths.get(System.getProperty("user.home"), "vlDiag_metadata.json"); + + ComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + diagram.writeSvg("", new DefaultSVGWriter(componentLibrary, layoutParameters), + new DefaultSubstationDiagramInitialValueProvider(network), + new NominalVoltageSubstationDiagramStyleProvider(), + new DefaultNodeLabelConfiguration(componentLibrary), + pathSVG, + false); + Assert.assertTrue(Files.exists(pathSVG)); + Assert.assertTrue(Files.exists(pathMetadata)); + try { + String refSvg = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/vlDiag.svg")), StandardCharsets.UTF_8)); + String svg = normalizeLineSeparator(new String(Files.readAllBytes(pathSVG), StandardCharsets.UTF_8)); + assertEquals(refSvg, svg); + Files.deleteIfExists(pathSVG); + + String refMetadata = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/vlDiag_metadata.json")), StandardCharsets.UTF_8)); + String metadata = normalizeLineSeparator(new String(Files.readAllBytes(pathMetadata), StandardCharsets.UTF_8)); + assertEquals(refMetadata, metadata); + Files.deleteIfExists(pathMetadata); + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1BusBreaker.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1BusBreaker.java new file mode 100644 index 000000000..a7e85140b --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1BusBreaker.java @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayout; +import com.powsybl.sld.model.Graph; +import org.junit.Before; +import org.junit.Test; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase1BusBreaker extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("busBreakerTestCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.BUS_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.BusBreakerView view = vl.getBusBreakerView(); + view.newBus() + .setId("b1") + .add(); + view.newBus() + .setId("b2") + .add(); + Load l = vl.newLoad() + .setId("l") + .setConnectableBus("b1") + .setBus("b1") + .setP0(10) + .setQ0(10) + .add(); + } + + @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + // build blocks + new BlockOrganizer().organize(g); + + // calculate coordinates + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + new PositionVoltageLevelLayout(g).run(layoutParameters); + + // write SVG and compare to reference + compareSvg(g, layoutParameters, "/TestCase1BusBreaker.svg"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1inverted.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1inverted.java new file mode 100644 index 000000000..db282a05a --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase1inverted.java @@ -0,0 +1,168 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayout; +import com.powsybl.sld.model.*; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + *
+ * l
+ * |
+ * b
+ * |
+ * d
+ * |
+ * ------ bbs
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase1inverted extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + Load l = vl.newLoad() + .setId("l") + .setNode(0) + .setP0(10) + .setQ0(10) + .add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, new ConnectablePosition + .Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newDisconnector() + .setId("d") + .setNode1(2) + .setNode2(1) + .add(); + view.newBreaker() + .setId("b") + .setNode1(1) + .setNode2(0) + .add(); + BusbarSection bbs = view.newBusbarSection() + .setId("bbs") + .setNode(2) + .add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + } + + @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + + // assert graph structure + assertEquals(5, g.getNodes().size()); + + assertEquals("l", g.getNodes().get(0).getId()); + assertEquals("bbs", g.getNodes().get(1).getId()); + assertEquals("FICT_vl_1", g.getNodes().get(2).getId()); + assertEquals("d", g.getNodes().get(3).getId()); + assertEquals("b", g.getNodes().get(4).getId()); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + // assert cells + assertEquals(1, g.getCells().size()); + Cell cell = g.getCells().iterator().next(); + assertEquals(Cell.CellType.EXTERN, cell.getType()); + assertEquals(5, cell.getNodes().size()); + assertTrue(((BusCell) cell).getPrimaryLegBlocks().isEmpty()); + assertEquals(1, ((BusCell) cell).getBusNodes().size()); + assertEquals("bbs", ((BusCell) cell).getBusNodes().get(0).getId()); + assertNull(cell.getRootBlock()); + assertEquals("EXTERN[FICT_vl_dFictif, b, bbs, d, l]", cell.getFullId()); + assertEquals(new Position(0, 0), ((BusCell) cell).getMaxBusPosition()); + + // build blocks + new BlockOrganizer().organize(g); + + // assert blocks and nodes rotation + assertNotNull(cell.getRootBlock()); + assertTrue(cell.getRootBlock() instanceof SerialBlock); + SerialBlock bc = (SerialBlock) cell.getRootBlock(); + assertEquals(new Position(0, 0, 1, 2, false, Orientation.VERTICAL), bc.getPosition()); + assertEquals("bbs", bc.getStartingNode().getId()); + assertEquals("l", bc.getEndingNode().getId()); + assertEquals(1, ((BusCell) cell).getPrimaryLegBlocks().size()); + + assertTrue(bc.getUpperBlock() instanceof BodyPrimaryBlock); + BodyPrimaryBlock ub = (BodyPrimaryBlock) bc.getUpperBlock(); + assertEquals(new Position(0, 0, 1, 2, false, Orientation.VERTICAL), ub.getPosition()); + assertEquals("FICT_vl_dFictif", ub.getStartingNode().getId()); + assertEquals("l", ub.getEndingNode().getId()); + assertTrue(ub.getStackableBlocks().isEmpty()); + + assertTrue(bc.getLowerBlock() instanceof LegPrimaryBlock); + LegPrimaryBlock lb = (LegPrimaryBlock) bc.getLowerBlock(); + assertEquals(new Position(0, 0, 1, 0, false, Orientation.VERTICAL), lb.getPosition()); + assertEquals("bbs", lb.getStartingNode().getId()); + assertEquals("FICT_vl_dFictif", lb.getEndingNode().getId()); + assertTrue(lb.getStackableBlocks().isEmpty()); + + // calculate coordinates + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + new PositionVoltageLevelLayout(g).run(layoutParameters); + + // assert coordinate + assertEquals(25, g.getNodes().get(0).getX(), 0); + assertEquals(-20, g.getNodes().get(0).getY(), 0); + assertEquals(10, g.getNodes().get(1).getX(), 0); + assertEquals(260, g.getNodes().get(1).getY(), 0); + assertEquals(25, g.getNodes().get(2).getX(), 0); + assertEquals(260, g.getNodes().get(2).getY(), 0); + assertEquals(25, g.getNodes().get(3).getX(), 0); + assertEquals(105, g.getNodes().get(3).getY(), 0); + assertEquals(25, g.getNodes().get(4).getX(), 0); + assertEquals(230, g.getNodes().get(4).getY(), 0); + + // write SVG and compare to reference + compareSvg(g, layoutParameters, "/TestCase1inverted.svg"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase2StackedCell.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase2StackedCell.java new file mode 100644 index 000000000..02fd7b4ab --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase2StackedCell.java @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayout; +import com.powsybl.sld.model.*; +import org.junit.Before; +import org.junit.Test; + +import static com.powsybl.sld.library.ComponentTypeName.NODE; +import static org.junit.Assert.*; + +/** + *
+ *     l
+ *     |
+ *     b
+ *    / \
+ *   |   |
+ * -d1---|---- bbs1
+ * -----d2---- bbs2
+ *
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase2StackedCell extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + BusbarSection bbs1 = view.newBusbarSection() + .setId("bbs1") + .setNode(0) + .add(); + bbs1.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs1, 1, 1)); + BusbarSection bbs2 = view.newBusbarSection() + .setId("bbs2") + .setNode(1) + .add(); + bbs2.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs2, 2, 1)); + Load l = vl.newLoad() + .setId("l") + .setNode(3) + .setP0(10) + .setQ0(10) + .add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, new ConnectablePosition + .Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newDisconnector() + .setId("d1") + .setNode1(0) + .setNode2(2) + .add(); + view.newDisconnector() + .setId("d2") + .setNode1(1) + .setNode2(2) + .add(); + view.newBreaker() + .setId("b") + .setNode1(2) + .setNode2(3) + .add(); + } + + @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + + // assert graph structure + assertEquals(7, g.getNodes().size()); + assertEquals(Node.NodeType.FICTITIOUS, g.getNodes().get(3).getType()); + assertEquals("FICT_vl_2", g.getNodes().get(3).getId()); + assertEquals(NODE, g.getNodes().get(3).getComponentType()); + + assertEquals(6, g.getEdges().size()); + assertEquals("d1", g.getEdges().get(1).getNode1().getId()); + assertEquals("FICT_vl_2", g.getEdges().get(1).getNode2().getId()); + assertEquals("d2", g.getEdges().get(3).getNode1().getId()); + assertEquals("FICT_vl_2", g.getEdges().get(3).getNode2().getId()); + assertEquals("FICT_vl_2", g.getEdges().get(4).getNode1().getId()); + assertEquals("b", g.getEdges().get(4).getNode2().getId()); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + // assert cells + assertEquals(1, g.getCells().size()); + Cell cell = g.getCells().iterator().next(); + assertEquals(Cell.CellType.EXTERN, cell.getType()); + assertEquals(-1, ((ExternCell) cell).getOrder()); + assertEquals(7, cell.getNodes().size()); + assertEquals(2, ((BusCell) cell).getBusNodes().size()); + assertEquals("EXTERN[FICT_vl_2, b, bbs1, bbs2, d1, d2, l]", cell.getFullId()); + + // build blocks + new BlockOrganizer().organize(g); + + // assert blocks and nodes rotation + assertEquals(2, ((BusCell) cell).getPrimaryLegBlocks().size()); + assertNotNull(cell.getRootBlock()); + assertTrue(cell.getRootBlock() instanceof SerialBlock); + SerialBlock bc = (SerialBlock) cell.getRootBlock(); + assertEquals(new Coord(-1, -1), bc.getCoord()); + assertEquals(new Position(0, 0, 1, 2, false, Orientation.VERTICAL), bc.getPosition()); + assertEquals("bbs1", bc.getStartingNode().getId()); + assertEquals("l", bc.getEndingNode().getId()); + + assertTrue(bc.getUpperBlock() instanceof BodyPrimaryBlock); + BodyPrimaryBlock bpy = (BodyPrimaryBlock) bc.getUpperBlock(); + assertEquals(bc, bpy.getParentBlock()); + assertEquals(new Coord(-1, -1), bpy.getCoord()); + assertEquals(new Position(0, 0, 1, 2, false, Orientation.VERTICAL), bpy.getPosition()); + assertEquals("FICT_vl_2", bpy.getStartingNode().getId()); + assertEquals("l", bpy.getEndingNode().getId()); + assertTrue(bpy.getStackableBlocks().isEmpty()); + + assertTrue(bc.getLowerBlock() instanceof LegParralelBlock); + LegParralelBlock bpl = (LegParralelBlock) bc.getLowerBlock(); + assertEquals(bc, bpl.getParentBlock()); + assertEquals(new Position(0, 0, 1, 0, false, Orientation.VERTICAL), bpl.getPosition()); + assertEquals(new Coord(-1, -1), bpl.getCoord()); + assertEquals("bbs1", bpl.getStartingNode().getId()); + assertEquals("FICT_vl_2", bpl.getEndingNode().getId()); + assertEquals(2, bpl.getSubBlocks().size()); + + assertTrue(bpl.getSubBlocks().get(0) instanceof LegPrimaryBlock); + LegPrimaryBlock bpy1 = (LegPrimaryBlock) bpl.getSubBlocks().get(0); + assertEquals(new Position(0, 0, 0, 0, false, Orientation.VERTICAL), bpy1.getPosition()); + assertEquals(new Coord(-1, -1), bpy1.getCoord()); + assertEquals("FICT_vl_2", bpy1.getEndingNode().getId()); + assertEquals("bbs1", bpy1.getStartingNode().getId()); + assertEquals(1, bpy1.getStackableBlocks().size()); + + assertTrue(bpl.getSubBlocks().get(1) instanceof LegPrimaryBlock); + LegPrimaryBlock bpy2 = (LegPrimaryBlock) bpl.getSubBlocks().get(1); + assertEquals(new Position(0, 0, 0, 0, false, Orientation.VERTICAL), bpy2.getPosition()); + assertEquals(new Coord(-1, -1), bpy2.getCoord()); + assertEquals("FICT_vl_2", bpy2.getEndingNode().getId()); + assertEquals("bbs2", bpy2.getStartingNode().getId()); + assertEquals(1, bpy2.getStackableBlocks().size()); + + // calculate coordinates + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + new PositionVoltageLevelLayout(g).run(layoutParameters); + + // assert coordinate + assertEquals(10, g.getNodes().get(0).getX(), 0); + assertEquals(260, g.getNodes().get(0).getY(), 0); + assertEquals(10, g.getNodes().get(1).getX(), 0); + assertEquals(285, g.getNodes().get(1).getY(), 0); + assertEquals(25, g.getNodes().get(2).getX(), 0); + assertEquals(-20, g.getNodes().get(2).getY(), 0); + assertEquals(25, g.getNodes().get(3).getX(), 0); + assertEquals(230, g.getNodes().get(3).getY(), 0); + assertEquals(25, g.getNodes().get(4).getX(), 0); + assertEquals(260, g.getNodes().get(4).getY(), 0); + assertEquals(25, g.getNodes().get(5).getX(), 0); + assertEquals(285, g.getNodes().get(5).getY(), 0); + assertEquals(25, g.getNodes().get(6).getX(), 0); + assertEquals(105, g.getNodes().get(6).getY(), 0); + + // write SVG and compare to reference + compareSvg(g, layoutParameters, "/TestCase2StackedCell.svg"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase3Coupling.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase3Coupling.java new file mode 100644 index 000000000..1a63cf216 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase3Coupling.java @@ -0,0 +1,184 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayout; +import com.powsybl.sld.model.*; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + *
+ *     b
+ *    / \
+ *   |   |
+ * -d1---|---- bbs1
+ * -----d2---- bbs2
+ *
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase3Coupling extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + + BusbarSection bbs1 = view.newBusbarSection() + .setId("bbs1") + .setNode(0) + .add(); + bbs1.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs1, 1, 1)); + + view.newDisconnector() + .setId("d1") + .setNode1(0) + .setNode2(1) + .add(); + + view.newBreaker() + .setId("b") + .setNode1(1) + .setNode2(2) + .add(); + + view.newDisconnector() + .setId("d2") + .setNode1(2) + .setNode2(3) + .add(); + + BusbarSection bbs2 = view.newBusbarSection() + .setId("bbs2") + .setNode(3) + .add(); + bbs2.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs2, 2, 1)); + + } + + @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + + // assert graph structure + assertEquals(7, g.getNodes().size()); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + // assert cells + assertEquals(1, g.getCells().size()); + Cell cell = g.getCells().iterator().next(); + assertEquals(Cell.CellType.INTERN, cell.getType()); + assertEquals(7, cell.getNodes().size()); + assertEquals(2, ((BusCell) cell).getBusNodes().size()); + assertEquals("INTERN[FICT_vl_d1Fictif, FICT_vl_d2Fictif, b, bbs1, bbs2, d1, d2]", cell.getFullId()); + + // build blocks + new BlockOrganizer().organize(g); + + // assert blocks and nodes rotation + assertEquals(2, ((BusCell) cell).getPrimaryLegBlocks().size()); + assertNotNull(cell.getRootBlock()); + assertTrue(cell.getRootBlock() instanceof SerialBlock); + SerialBlock bp = (SerialBlock) cell.getRootBlock(); + assertEquals(new Position(-1, -1, 0, 0, false, Orientation.VERTICAL), bp.getPosition()); + assertEquals("bbs1", bp.getStartingNode().getId()); + assertEquals("FICT_vl_d2Fictif", bp.getEndingNode().getId()); + assertEquals(3, bp.getSubBlocks().size()); + + assertTrue(bp.getSubBlocks().get(0) instanceof LegPrimaryBlock); + + LegPrimaryBlock bc = (LegPrimaryBlock) bp.getSubBlocks().get(0); + assertEquals(new Position(0, 0, 1, 0, false, Orientation.VERTICAL), bc.getPosition()); + + assertTrue(bp.getSubBlocks().get(1) instanceof BodyPrimaryBlock); + BodyPrimaryBlock bpyl = (BodyPrimaryBlock) bp.getSubBlocks().get(1); + assertEquals(Node.NodeType.FICTITIOUS, bpyl.getStartingNode().getType()); + assertEquals(new Position(0, 1, 0, 0, false, Orientation.VERTICAL), bpyl.getPosition()); + + assertTrue(bp.getSubBlocks().get(2) instanceof LegPrimaryBlock); + LegPrimaryBlock bpyu = (LegPrimaryBlock) bp.getSubBlocks().get(2); + assertEquals(Node.NodeType.SWITCH, bpyu.getNodes().get(1).getType()); + assertEquals(new Position(1, 2, 1, 0, false, Orientation.VERTICAL), bpyu.getPosition()); + + // calculate coordinates + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + new PositionVoltageLevelLayout(g).run(layoutParameters); + + // assert coordinate + assertEquals(10, g.getNodes().get(0).getX(), 0); + assertEquals(260, g.getNodes().get(0).getY(), 0); + assertFalse(g.getNodes().get(0).isRotated()); + + assertEquals(10, g.getNodes().get(1).getX(), 0); + assertEquals(285, g.getNodes().get(1).getY(), 0); + assertFalse(g.getNodes().get(1).isRotated()); + + assertEquals(25, g.getNodes().get(2).getX(), 0); + assertEquals(260, g.getNodes().get(2).getY(), 0); + assertFalse(g.getNodes().get(2).isRotated()); + + assertEquals(50, g.getNodes().get(3).getX(), 0); + assertEquals(220, g.getNodes().get(3).getY(), 0); + assertTrue(g.getNodes().get(3).isRotated()); + + assertEquals(75, g.getNodes().get(4).getX(), 0); + assertEquals(285, g.getNodes().get(4).getY(), 0); + assertFalse(g.getNodes().get(4).isRotated()); + + assertEquals(25, g.getNodes().get(5).getX(), 0); + assertEquals(220, g.getNodes().get(5).getY(), 0); + assertTrue(g.getNodes().get(5).isRotated()); + + assertEquals(75, g.getNodes().get(6).getX(), 0); + assertEquals(220, g.getNodes().get(6).getY(), 0); + assertTrue(g.getNodes().get(6).isRotated()); + + // write SVG and compare to reference + compareSvg(g, layoutParameters, "/TestCase3Coupling.svg"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase4NotParallelel.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase4NotParallelel.java new file mode 100644 index 000000000..c582800ed --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase4NotParallelel.java @@ -0,0 +1,249 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayout; +import com.powsybl.sld.model.*; +import org.junit.Before; +import org.junit.Test; + +import java.util.Iterator; + +import static org.junit.Assert.*; + + +/** + *
+ *            la                        gc
+ *             |                        |
+ *            ba                        bc
+ *           /  \                       |
+ *          |    |                      |
+ * bbs1.1 -da1---|--- ss1 --db1--------dc- bbs1.2
+ * bbs2.1 ------da2----------|---db2------
+ *                           |    |
+ *                            \  /
+ *                             bb
+ *                              |
+ *                             lb
+ *
+ * 
+ *

+ * the branch c is to cover the merging part of SubSections class (and use of generator) + * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase4NotParallelel extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + + BusbarSection bbs11 = view.newBusbarSection() + .setId("bbs1.1") + .setNode(0) + .add(); + bbs11.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs11, 1, 1)); + + BusbarSection bbs12 = view.newBusbarSection() + .setId("bbs1.2") + .setNode(1) + .add(); + bbs12.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs12, 1, 2)); + + BusbarSection bbs21 = view.newBusbarSection() + .setId("bbs2.1") + .setNode(2) + .add(); + bbs21.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs21, 2, 1)); + + Load la = vl.newLoad() + .setId("la") + .setNode(3) + .setP0(10) + .setQ0(10) + .add(); + la.addExtension(ConnectablePosition.class, new ConnectablePosition<>(la, new ConnectablePosition + .Feeder("la", 10, ConnectablePosition.Direction.TOP), null, null, null)); + + view.newBreaker() + .setId("ba") + .setNode1(3) + .setNode2(4) + .add(); + + view.newDisconnector() + .setId("da1") + .setNode1(4) + .setNode2(0) + .add(); + + view.newDisconnector() + .setId("da2") + .setNode1(4) + .setNode2(2) + .add(); + + Load lb = vl.newLoad() + .setId("lb") + .setNode(5) + .setP0(10) + .setQ0(10) + .add(); + lb.addExtension(ConnectablePosition.class, new ConnectablePosition<>(lb, new ConnectablePosition + .Feeder("lb", 20, ConnectablePosition.Direction.BOTTOM), null, null, null)); + + view.newBreaker() + .setId("bb") + .setNode1(5) + .setNode2(6) + .add(); + + view.newDisconnector() + .setId("db1") + .setNode1(6) + .setNode2(1) + .add(); + + view.newDisconnector() + .setId("db2") + .setNode1(6) + .setNode2(2) + .add(); + view.newDisconnector() + .setId("ss1") + .setNode1(1) + .setNode2(0) + .add(); + + Generator gc = vl.newGenerator() + .setId("gc") + .setNode(7) + .setMinP(0) + .setMaxP(20) + .setVoltageRegulatorOn(false) + .setTargetP(10) + .setTargetQ(10) + .add(); + gc.addExtension(ConnectablePosition.class, new ConnectablePosition<>(gc, new ConnectablePosition + .Feeder("gc", 30, ConnectablePosition.Direction.TOP), null, null, null)); + + view.newBreaker() + .setId("bc") + .setNode1(7) + .setNode2(8) + .add(); + + view.newDisconnector() + .setId("dc1") + .setNode1(8) + .setNode2(1) + .add(); + } + + @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + + // assert graph structure + assertEquals(18, g.getNodes().size()); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + // assert cells + assertEquals(4, g.getCells().size()); + Iterator it = g.getCells().iterator(); + Cell cell = it.next(); + assertEquals(Cell.CellType.INTERN, cell.getType()); + assertEquals(3, cell.getNodes().size()); + assertEquals(2, ((BusCell) cell).getBusNodes().size()); + assertEquals("INTERN[bbs1.1, bbs1.2, ss1]", cell.getFullId()); + + // build blocks + new BlockOrganizer().organize(g); + + // assert blocks and nodes rotation + assertEquals(2, ((BusCell) cell).getPrimaryLegBlocks().size()); + assertNotNull(cell.getRootBlock()); + assertTrue(cell.getRootBlock() instanceof SerialBlock); + assertEquals(new Position(-1, -1, 0, 0, false, Orientation.HORIZONTAL), cell.getRootBlock().getPosition()); + + cell = it.next(); + assertTrue(cell.getRootBlock() instanceof SerialBlock); + assertEquals(BusCell.Direction.TOP, ((BusCell) cell).getDirection()); + SerialBlock bc = (SerialBlock) cell.getRootBlock(); + assertEquals(new Position(0, 0, 1, 2, false, Orientation.VERTICAL), bc.getPosition()); + + BodyPrimaryBlock byu = (BodyPrimaryBlock) bc.getUpperBlock(); + assertEquals(new Position(0, 0, 1, 2, false, Orientation.VERTICAL), byu.getPosition()); + + LegParralelBlock bpl = (LegParralelBlock) bc.getLowerBlock(); + assertEquals(new Position(0, 0, 1, 0, false, Orientation.VERTICAL), bpl.getPosition()); + + cell = it.next(); + assertTrue(cell.getRootBlock() instanceof SerialBlock); + assertEquals(BusCell.Direction.BOTTOM, ((BusCell) cell).getDirection()); + bc = (SerialBlock) cell.getRootBlock(); + assertEquals(new Position(2, 0, 1, 2, false, Orientation.VERTICAL), bc.getPosition()); + + byu = (BodyPrimaryBlock) bc.getUpperBlock(); + assertEquals(new Position(0, 0, 1, 2, false, Orientation.VERTICAL), byu.getPosition()); + + bpl = (LegParralelBlock) bc.getLowerBlock(); + assertEquals(new Position(0, 0, 1, 0, false, Orientation.VERTICAL), bpl.getPosition()); + + cell = it.next(); + assertTrue(cell.getRootBlock() instanceof SerialBlock); + assertEquals(BusCell.Direction.TOP, ((BusCell) cell).getDirection()); + SerialBlock bc3 = (SerialBlock) cell.getRootBlock(); + assertEquals(new Position(3, 0, 1, 2, false, Orientation.VERTICAL), bc3.getPosition()); + + // calculate coordinates + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + new PositionVoltageLevelLayout(g).run(layoutParameters); + + // write SVG and compare to reference + compareSvg(g, layoutParameters, "/TestCase4NotParallelel.svg"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase5ShuntHorizontal.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase5ShuntHorizontal.java new file mode 100644 index 000000000..ccb3c4800 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase5ShuntHorizontal.java @@ -0,0 +1,197 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayout; +import com.powsybl.sld.model.*; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.util.Iterator; + +import static org.junit.Assert.*; + +/** + *

+ *
+ *       la     lb
+ *       |      |
+ *      nsa-bs-nsb
+ *       |      |
+ *       ba     bb
+ *       |      |
+ * bbs---da-----db---
+ *
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase5ShuntHorizontal extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + + BusbarSection bbs11 = view.newBusbarSection() + .setId("bbs") + .setNode(0) + .add(); + bbs11.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs11, 1, 1)); + + Load la = vl.newLoad() + .setId("la") + .setNode(2) + .setP0(10) + .setQ0(10) + .add(); + la.addExtension(ConnectablePosition.class, new ConnectablePosition<>(la, new ConnectablePosition + .Feeder("la", 10, ConnectablePosition.Direction.TOP), null, null, null)); + + view.newBreaker() + .setId("ba") + .setNode1(2) + .setNode2(1) + .add(); + + view.newDisconnector() + .setId("da") + .setNode1(1) + .setNode2(0) + .add(); + + Load lb = vl.newLoad() + .setId("lb") + .setNode(4) + .setP0(10) + .setQ0(10) + .add(); + lb.addExtension(ConnectablePosition.class, new ConnectablePosition<>(lb, new ConnectablePosition + .Feeder("lb", 20, ConnectablePosition.Direction.TOP), null, null, null)); + + view.newBreaker() + .setId("bb") + .setNode1(4) + .setNode2(3) + .add(); + + view.newDisconnector() + .setId("db") + .setNode1(3) + .setNode2(0) + .add(); + + view.newBreaker() + .setId("bs") + .setNode1(2) + .setNode2(4) + .add(); + } + + @Test + public void test() throws IOException { + // build graph + Graph g = Graph.create(vl); + + // assert graph structure + assertEquals(10, g.getNodes().size()); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + // assert cells + assertEquals(3, g.getCells().size()); + Iterator it = g.getCells().iterator(); + Cell cellb = it.next(); + assertEquals(Cell.CellType.EXTERN, cellb.getType()); + assertEquals(6, cellb.getNodes().size()); + assertEquals("EXTERN[FICT_vl_dbFictif, FICT_vl_lbFictif, bb, bbs, db, lb]", cellb.getFullId()); + + Cell cellShunt = it.next(); + assertEquals(Cell.CellType.SHUNT, cellShunt.getType()); + assertEquals(3, cellShunt.getNodes().size()); + assertEquals("SHUNT[FICT_vl_laFictif, FICT_vl_lbFictif, bs]", cellShunt.getFullId()); + + Cell cella = it.next(); + assertEquals(Cell.CellType.EXTERN, cella.getType()); + assertEquals(6, cella.getNodes().size()); + assertEquals("EXTERN[FICT_vl_daFictif, FICT_vl_laFictif, ba, bbs, da, la]", cella.getFullId()); + + // build blocks + new BlockOrganizer().organize(g); + + // assert blocks and nodes rotation + assertEquals(1, ((BusCell) cella).getPrimaryLegBlocks().size()); + assertNotNull(cella.getRootBlock()); + assertTrue(cella.getRootBlock() instanceof SerialBlock); + assertEquals(new Position(0, 0, 1, 3, false, Orientation.VERTICAL), cella.getRootBlock().getPosition()); + + assertEquals(1, ((BusCell) cellb).getPrimaryLegBlocks().size()); + assertNotNull(cellb.getRootBlock()); + assertTrue(cellb.getRootBlock() instanceof SerialBlock); + assertEquals(new Position(1, 0, 1, 3, false, Orientation.VERTICAL), cellb.getRootBlock().getPosition()); + + assertNotNull(cellShunt.getRootBlock()); + assertTrue(cellShunt.getRootBlock() instanceof BodyPrimaryBlock); + assertEquals(new Position(-1, -1, 0, 0, false, Orientation.HORIZONTAL), cellShunt.getRootBlock().getPosition()); + + // calculate coordinates + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + new PositionVoltageLevelLayout(g).run(layoutParameters); + + // assert coordinate + assertEquals(50, g.getNodes().get(7).getX(), 0); + assertEquals(63.3, g.getNodes().get(7).getY(), 0.1); + assertTrue(g.getNodes().get(7).isRotated()); + + assertEquals(25, g.getNodes().get(8).getX(), 0); + assertEquals(63.3, g.getNodes().get(8).getY(), 0.1); + assertFalse(g.getNodes().get(8).isRotated()); + + assertEquals(75, g.getNodes().get(9).getX(), 0); + assertEquals(63.3, g.getNodes().get(9).getY(), 0.1); + assertFalse(g.getNodes().get(9).isRotated()); + + // write SVG and compare to reference + compareSvg(g, layoutParameters, "/TestCase5ShuntHorizontal.svg"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase5ShuntVertical.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase5ShuntVertical.java new file mode 100644 index 000000000..beaf5c960 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase5ShuntVertical.java @@ -0,0 +1,210 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayout; +import com.powsybl.sld.model.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.util.Iterator; + +import static org.junit.Assert.*; + +/** + *
+ *
+ *       la     lb
+ *       |      |
+ *      nsa- |  bb
+ *       |  bs  |
+ *       ba  |- nsb
+ *       |      |
+ * bbs---da-----db---
+ *
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase5ShuntVertical extends AbstractTestCase { + + private FileSystem fileSystem; + + @Before + public void setUp() { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + + network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + + BusbarSection bbs11 = view.newBusbarSection() + .setId("bbs") + .setNode(0) + .add(); + bbs11.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs11, 1, 1)); + + Load la = vl.newLoad() + .setId("la") + .setNode(2) + .setP0(10) + .setQ0(10) + .add(); + la.addExtension(ConnectablePosition.class, new ConnectablePosition<>(la, new ConnectablePosition + .Feeder("la", 10, ConnectablePosition.Direction.TOP), null, null, null)); + + view.newBreaker() + .setId("ba") + .setNode1(2) + .setNode2(1) + .add(); + + view.newDisconnector() + .setId("da") + .setNode1(1) + .setNode2(0) + .add(); + + Load lb = vl.newLoad() + .setId("lb") + .setNode(4) + .setP0(10) + .setQ0(10) + .add(); + lb.addExtension(ConnectablePosition.class, new ConnectablePosition<>(lb, new ConnectablePosition + .Feeder("lb", 20, ConnectablePosition.Direction.TOP), null, null, null)); + + view.newBreaker() + .setId("bb") + .setNode1(4) + .setNode2(3) + .add(); + + view.newDisconnector() + .setId("db") + .setNode1(3) + .setNode2(0) + .add(); + + view.newBreaker() + .setId("bs") + .setNode1(2) + .setNode2(3) + .add(); + } + + @After + public void tearDown() throws Exception { + fileSystem.close(); + } + + @Test + public void test() throws IOException { + // build graph + Graph g = Graph.create(vl); + + // assert graph structure + assertEquals(10, g.getNodes().size()); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + // assert cells + assertEquals(3, g.getCells().size()); + Iterator it = g.getCells().iterator(); + Cell cellb = it.next(); + assertEquals(Cell.CellType.EXTERN, cellb.getType()); + assertEquals(5, cellb.getNodes().size()); + assertEquals("EXTERN[FICT_vl_3, bb, bbs, db, lb]", cellb.getFullId()); + + Cell cellShunt = it.next(); + assertEquals(Cell.CellType.SHUNT, cellShunt.getType()); + assertEquals(3, cellShunt.getNodes().size()); + assertEquals("SHUNT[FICT_vl_3, FICT_vl_laFictif, bs]", cellShunt.getFullId()); + + Cell cella = it.next(); + assertEquals(Cell.CellType.EXTERN, cella.getType()); + assertEquals(6, cella.getNodes().size()); + assertEquals("EXTERN[FICT_vl_daFictif, FICT_vl_laFictif, ba, bbs, da, la]", cella.getFullId()); + + // build blocks + new BlockOrganizer().organize(g); + + // assert blocks and nodes rotation + assertEquals(1, ((BusCell) cella).getPrimaryLegBlocks().size()); + assertNotNull(cella.getRootBlock()); + assertTrue(cella.getRootBlock() instanceof SerialBlock); + assertEquals(new Position(0, 0, 1, 3, false, Orientation.VERTICAL), cella.getRootBlock().getPosition()); + + assertEquals(1, ((BusCell) cellb).getPrimaryLegBlocks().size()); + assertNotNull(cellb.getRootBlock()); + assertTrue(cellb.getRootBlock() instanceof SerialBlock); + assertEquals(new Position(1, 0, 1, 2, false, Orientation.VERTICAL), cellb.getRootBlock().getPosition()); + + assertNotNull(cellShunt.getRootBlock()); + assertTrue(cellShunt.getRootBlock() instanceof BodyPrimaryBlock); + assertEquals(new Position(-1, -1, 0, 0, false, Orientation.HORIZONTAL), cellShunt.getRootBlock().getPosition()); + + // calculate coordinates + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + new PositionVoltageLevelLayout(g).run(layoutParameters); + + // assert coordinate + assertEquals(75, g.getNodes().get(7).getX(), 0); + assertEquals(260, g.getNodes().get(7).getY(), 0.1); + assertFalse(g.getNodes().get(7).isRotated()); + + assertEquals(50, g.getNodes().get(8).getX(), 0); + assertEquals(146.6, g.getNodes().get(8).getY(), 0.1); + assertFalse(g.getNodes().get(8).isRotated()); + + assertEquals(25, g.getNodes().get(9).getX(), 0); + assertEquals(63.3, g.getNodes().get(9).getY(), 0.1); + assertFalse(g.getNodes().get(9).isRotated()); + + // write SVG and compare to reference + compareSvg(g, layoutParameters, "/TestCase5ShuntVertical.svg"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase6CouplingNonFlatHorizontal.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase6CouplingNonFlatHorizontal.java new file mode 100644 index 000000000..31ca09491 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase6CouplingNonFlatHorizontal.java @@ -0,0 +1,208 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayout; +import com.powsybl.sld.model.*; +import org.junit.Before; +import org.junit.Test; + +import java.util.Iterator; + +import static org.junit.Assert.*; + +/** + *
+ *              b
+ *           /     \
+ *          |       |
+ * bbs1.1 -d1- ds1 -|-- bbs1.2
+ * bbs2.1 ---- ds2 -d2- bbs2.2
+ *
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase6CouplingNonFlatHorizontal extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + + BusbarSection bbs11 = view.newBusbarSection() + .setId("bbs1.1") + .setNode(0) + .add(); + bbs11.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs11, 1, 1)); + + BusbarSection bbs12 = view.newBusbarSection() + .setId("bbs1.2") + .setNode(1) + .add(); + bbs12.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs12, 1, 2)); + + BusbarSection bbs21 = view.newBusbarSection() + .setId("bbs2.1") + .setNode(2) + .add(); + bbs21.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs21, 2, 1)); + + BusbarSection bbs22 = view.newBusbarSection() + .setId("bbs2.2") + .setNode(3) + .add(); + bbs22.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs22, 2, 2)); + + view.newDisconnector() + .setId("d1") + .setNode1(0) + .setNode2(4) + .add(); + + view.newBreaker() + .setId("b") + .setNode1(4) + .setNode2(5) + .add(); + + view.newDisconnector() + .setId("d2") + .setNode1(5) + .setNode2(3) + .add(); + + view.newDisconnector() + .setId("ds1") + .setNode1(0) + .setNode2(1) + .add(); + + view.newDisconnector() + .setId("ds2") + .setNode1(2) + .setNode2(3) + .add(); + } + + @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + + // assert graph structure + assertEquals(11, g.getNodes().size()); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + assertEquals(11, g.getNodes().size()); + + // assert cells + assertEquals(3, g.getCells().size()); + Iterator it = g.getCells().iterator(); + Cell cellB = it.next(); + assertEquals("INTERN[FICT_vl_d1Fictif, FICT_vl_d2Fictif, b, bbs1.1, bbs2.2, d1, d2]", cellB.getFullId()); + Cell cellD1 = it.next(); + assertEquals("INTERN[bbs1.1, bbs1.2, ds1]", cellD1.getFullId()); + Cell cellD2 = it.next(); + assertEquals("INTERN[bbs2.1, bbs2.2, ds2]", cellD2.getFullId()); + + // build blocks + new BlockOrganizer().organize(g); + + // assert blocks and nodes rotation + assertEquals(2, ((BusCell) cellB).getPrimaryLegBlocks().size()); + assertNotNull(cellB.getRootBlock()); + assertTrue(cellB.getRootBlock() instanceof SerialBlock); + SerialBlock bp = (SerialBlock) cellB.getRootBlock(); + assertEquals(new Position(-1, -1, 0, 0, false, Orientation.VERTICAL), bp.getPosition()); + + assertTrue(bp.getSubBlocks().get(0) instanceof LegPrimaryBlock); + + LegPrimaryBlock bc = (LegPrimaryBlock) bp.getSubBlocks().get(0); + assertEquals(new Position(0, 0, 1, 0, false, Orientation.VERTICAL), bc.getPosition()); + + assertTrue(bp.getSubBlocks().get(1) instanceof BodyPrimaryBlock); + BodyPrimaryBlock bpyl = (BodyPrimaryBlock) bp.getSubBlocks().get(1); + assertEquals(new Position(0, 1, 0, 0, false, Orientation.VERTICAL), bpyl.getPosition()); + + assertTrue(bp.getSubBlocks().get(2) instanceof LegPrimaryBlock); + LegPrimaryBlock bpyu = (LegPrimaryBlock) bp.getSubBlocks().get(2); + assertEquals(new Position(2, 2, 1, 0, false, Orientation.VERTICAL), bpyu.getPosition()); + + // calculate coordinates + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + new PositionVoltageLevelLayout(g).run(layoutParameters); + + // assert coordinate + assertEquals(10, g.getNodes().get(0).getX(), 0); + assertEquals(260, g.getNodes().get(0).getY(), 0); + assertFalse(g.getNodes().get(0).isRotated()); + + assertEquals(110, g.getNodes().get(3).getX(), 0); + assertEquals(285, g.getNodes().get(3).getY(), 0); + assertFalse(g.getNodes().get(3).isRotated()); + + assertEquals(50, g.getNodes().get(5).getX(), 0); + assertEquals(220, g.getNodes().get(5).getY(), 0); + assertTrue(g.getNodes().get(5).isRotated()); + + assertEquals(25, g.getNodes().get(9).getX(), 0); + assertEquals(220, g.getNodes().get(9).getY(), 0); + assertTrue(g.getNodes().get(9).isRotated()); + + assertEquals(125, g.getNodes().get(10).getX(), 0); + assertEquals(220, g.getNodes().get(10).getY(), 0); + assertTrue(g.getNodes().get(10).isRotated()); + + assertEquals(75, g.getNodes().get(7).getX(), 0); + assertEquals(260, g.getNodes().get(7).getY(), 0); + assertTrue(g.getNodes().get(7).isRotated()); + + assertEquals(75, g.getNodes().get(8).getX(), 0); + assertEquals(285, g.getNodes().get(8).getY(), 0); + assertTrue(g.getNodes().get(8).isRotated()); + + // write SVG and compare to reference + compareSvg(g, layoutParameters, "/TestCase6CouplingNonFlatHorizontal.svg"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase7CellDetectionIssue.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase7CellDetectionIssue.java new file mode 100644 index 000000000..73e7fdac8 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase7CellDetectionIssue.java @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.model.Graph; +import org.junit.Before; + +import static org.junit.Assert.assertEquals; + +/** + *
+ * l
+ * |
+ * ------ bbs
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase7CellDetectionIssue extends AbstractTestCase { + + private VoltageLevel vl; + + @Before + public void setUp() { + network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + Load l = vl.newLoad() + .setId("l") + .setNode(0) + .setP0(10) + .setQ0(10) + .add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, new ConnectablePosition + .Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newInternalConnection() + .setNode1(0) + .setNode2(1) + .add(); + BusbarSection bbs = view.newBusbarSection() + .setId("bbs") + .setNode(1) + .add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + } + +// @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + + // assert graph structure + assertEquals(2, g.getNodes().size()); + + // detect cells + new ImplicitCellDetector().detectCells(g); + + // assert cells + assertEquals(1, g.getCells().size()); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase7DoubleDJ.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase7DoubleDJ.java new file mode 100644 index 000000000..d897664e3 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase7DoubleDJ.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; + +/** + *
+ *
+ *  bbs1---d1-b1-b2-d2--- bbs2
+ *
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase7DoubleDJ extends AbstractTestCase { + + @Override + void setUp() { + network = Network.create("testCase", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + + BusbarSection bbs1 = view.newBusbarSection() + .setId("bbs1") + .setNode(0) + .add(); + bbs1.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs1, 1, 1)); + + view.newDisconnector() + .setId("d1") + .setNode1(0) + .setNode2(1) + .add(); + + view.newBreaker() + .setId("b1") + .setNode1(1) + .setNode2(2) + .add(); + + view.newBreaker() + .setId("b2") + .setNode1(2) + .setNode2(3) + .add(); + + view.newDisconnector() + .setId("d2") + .setNode1(3) + .setNode2(4) + .add(); + + BusbarSection bbs2 = view.newBusbarSection() + .setId("bbs2") + .setNode(4) + .add(); + bbs2.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs2, 1, 2)); + + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase8JumpOverStacked.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase8JumpOverStacked.java new file mode 100644 index 000000000..eefb75f85 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase8JumpOverStacked.java @@ -0,0 +1,112 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; + +/** + *
+ *
+ *              *-----b-----*
+ *             /\          /\
+ *            |  |        |  |
+ *  bbs11 ---d11-|---d1--d12-|--- bbs12
+ *               |           |
+ *  bbs21 ------d21--d2-----d22-- bbs12
+ *
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase8JumpOverStacked extends AbstractTestCase { + + @Override + void setUp() { + network = Network.create("testCase", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(15); + + BusbarSection bbs11 = view.newBusbarSection() + .setId("bbs11") + .setNode(0) + .add(); + bbs11.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs11, 1, 1)); + + BusbarSection bbs12 = view.newBusbarSection() + .setId("bbs12") + .setNode(1) + .add(); + bbs12.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs12, 1, 2)); + + view.newDisconnector() + .setId("d1") + .setNode1(0) + .setNode2(1) + .add(); + + BusbarSection bbs21 = view.newBusbarSection() + .setId("bbs21") + .setNode(2) + .add(); + bbs21.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs21, 2, 1)); + + BusbarSection bbs22 = view.newBusbarSection() + .setId("bbs22") + .setNode(3) + .add(); + bbs22.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs22, 2, 2)); + + view.newDisconnector() + .setId("d2") + .setNode1(2) + .setNode2(3) + .add(); + + view.newDisconnector() + .setId("d11") + .setNode1(0) + .setNode2(4) + .add(); + + view.newDisconnector() + .setId("d12") + .setNode1(1) + .setNode2(5) + .add(); + + view.newDisconnector() + .setId("d21") + .setNode1(2) + .setNode2(4) + .add(); + + view.newDisconnector() + .setId("d22") + .setNode1(3) + .setNode2(5) + .add(); + + view.newBreaker() + .setId("b") + .setNode1(4) + .setNode2(5) + .add(); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase9singularInternCell.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase9singularInternCell.java new file mode 100644 index 000000000..28a13b8a8 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestCase9singularInternCell.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; + +/** + *
+ *
+ *
+ *     n
+ *    / \
+ *   |   |
+ * -d1---|---- bbs1
+ * -----d2---- bbs2
+ *
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class TestCase9singularInternCell extends AbstractTestCase { + + @Override + void setUp() { + network = Network.create("testCase1", "AbstractTest"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + BusbarSection bbs1 = view.newBusbarSection() + .setId("bbs1") + .setNode(0) + .add(); + bbs1.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs1, 1, 1)); + BusbarSection bbs2 = view.newBusbarSection() + .setId("bbs2") + .setNode(1) + .add(); + bbs2.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs2, 2, 1)); + view.newDisconnector() + .setId("d1") + .setNode1(0) + .setNode2(2) + .add(); + view.newDisconnector() + .setId("d2") + .setNode1(1) + .setNode2(2) + .add(); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestSerialBlock.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestSerialBlock.java new file mode 100644 index 000000000..dc85f3a8b --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestSerialBlock.java @@ -0,0 +1,151 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.model.*; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * + * @author Benoit Jeanson + */ +public class TestSerialBlock extends AbstractTestCase { + + @Before + public void setUp() { + Network network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + + BusbarSection bbs11 = view.newBusbarSection() + .setId("bbs") + .setNode(0) + .add(); + bbs11.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs11, 1, 1)); + + Load la = vl.newLoad() + .setId("la") + .setNode(2) + .setP0(10) + .setQ0(10) + .add(); + la.addExtension(ConnectablePosition.class, new ConnectablePosition<>(la, new ConnectablePosition + .Feeder("la", 10, ConnectablePosition.Direction.TOP), null, null, null)); + + view.newBreaker() + .setId("ba") + .setNode1(2) + .setNode2(1) + .add(); + + view.newDisconnector() + .setId("da") + .setNode1(1) + .setNode2(0) + .add(); + } + + @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + // detect cells + new ImplicitCellDetector().detectCells(g); + new BlockOrganizer().organize(g); + + assertEquals(1, g.getCells().size()); + Cell cell = g.getCells().iterator().next(); + assertEquals(Block.Type.SERIAL, cell.getRootBlock().getType()); + SerialBlock sb = (SerialBlock) cell.getRootBlock(); + assertTrue(sb.isEmbedingNodeType(Node.NodeType.BUS)); + assertTrue(sb.isEmbedingNodeType(Node.NodeType.FEEDER)); + assertTrue(sb.getLowerBlock().isEmbedingNodeType(Node.NodeType.BUS)); + assertTrue(sb.getUpperBlock().isEmbedingNodeType(Node.NodeType.FEEDER)); + assertTrue(sb.getSubBlocks().get(0).isEmbedingNodeType(Node.NodeType.BUS)); + assertTrue(sb.getSubBlocks().get(1).isEmbedingNodeType(Node.NodeType.FEEDER)); + + assertEquals("bbs", sb.getSubBlocks().get(0).getStartingNode().getId()); + assertEquals("FICT_vl_daFictif", sb.getSubBlocks().get(0).getEndingNode().getId()); + assertEquals("FICT_vl_daFictif", sb.getSubBlocks().get(1).getStartingNode().getId()); + assertEquals("la", sb.getSubBlocks().get(1).getEndingNode().getId()); + + sb.sizing(); + assertEquals(0, sb.getPosition().getH()); + assertEquals(0, sb.getPosition().getV()); + assertEquals(1, sb.getPosition().getHSpan()); + assertEquals(2, sb.getPosition().getVSpan()); + + assertEquals(0, sb.getLowerBlock().getPosition().getH()); + assertEquals(0, sb.getLowerBlock().getPosition().getV()); + assertEquals(1, sb.getLowerBlock().getPosition().getHSpan()); + assertEquals(0, sb.getLowerBlock().getPosition().getVSpan()); + + assertEquals(0, sb.getUpperBlock().getPosition().getH()); + assertEquals(0, sb.getUpperBlock().getPosition().getV()); + assertEquals(1, sb.getUpperBlock().getPosition().getHSpan()); + assertEquals(2, sb.getUpperBlock().getPosition().getVSpan()); + + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + sb.setX(10); + sb.setY(20); + sb.setXSpan(100); + sb.setYSpan(200); + sb.coordHorizontalCase(layoutParameters); + + assertEquals(10, sb.getLowerBlock().getCoord().getX(), 0); + assertEquals(20, sb.getLowerBlock().getCoord().getY(), 0); + assertEquals(100, sb.getLowerBlock().getCoord().getXSpan(), 0); + assertEquals(200, sb.getLowerBlock().getCoord().getYSpan(), 0); + + assertEquals(10, sb.getUpperBlock().getCoord().getX(), 0); + assertEquals(20, sb.getUpperBlock().getCoord().getY(), 0); + assertEquals(100, sb.getUpperBlock().getCoord().getXSpan(), 0); + assertEquals(200, sb.getUpperBlock().getCoord().getYSpan(), 0); + + sb.reverseBlock(); + + assertEquals("FICT_vl_daFictif", sb.getSubBlocks().get(1).getEndingNode().getId()); + assertEquals("bbs", sb.getSubBlocks().get(1).getStartingNode().getId()); + assertEquals("FICT_vl_daFictif", sb.getSubBlocks().get(0).getEndingNode().getId()); + assertEquals("la", sb.getSubBlocks().get(0).getStartingNode().getId()); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestSerialParallelBlock.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestSerialParallelBlock.java new file mode 100644 index 000000000..d2edc3e09 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestSerialParallelBlock.java @@ -0,0 +1,189 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.BlockOrganizer; +import com.powsybl.sld.layout.ImplicitCellDetector; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.model.*; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * + * @author Benoit Jeanson + */ +public class TestSerialParallelBlock extends AbstractTestCase { + + @Before + public void setUp() { + Network network = Network.create("testCase1", "test"); + Substation s = network.newSubstation() + .setId("s") + .setCountry(Country.FR) + .add(); + vl = s.newVoltageLevel() + .setId("vl") + .setTopologyKind(TopologyKind.NODE_BREAKER) + .setNominalV(400) + .add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView() + .setNodeCount(10); + + BusbarSection bbs11 = view.newBusbarSection() + .setId("bbs") + .setNode(0) + .add(); + bbs11.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs11, 1, 1)); + + view.newDisconnector() + .setId("da") + .setNode1(0) + .setNode2(1) + .add(); + + view.newBreaker() + .setId("ba") + .setNode1(1) + .setNode2(2) + .add(); + + view.newBreaker() + .setId("ba1") + .setNode1(2) + .setNode2(4) + .add(); + + view.newBreaker() + .setId("ba2") + .setNode1(2) + .setNode2(5) + .add(); + + Load la1 = vl.newLoad() + .setId("la1") + .setNode(4) + .setP0(10) + .setQ0(10) + .add(); + la1.addExtension(ConnectablePosition.class, new ConnectablePosition<>(la1, new ConnectablePosition + .Feeder("la1", 10, ConnectablePosition.Direction.TOP), null, null, null)); + + Load la2 = vl.newLoad() + .setId("la2") + .setNode(5) + .setP0(10) + .setQ0(10) + .add(); + la2.addExtension(ConnectablePosition.class, new ConnectablePosition<>(la2, new ConnectablePosition + .Feeder("la2", 10, ConnectablePosition.Direction.TOP), null, null, null)); + } + + @Test + public void test() { + // build graph + Graph g = Graph.create(vl); + // detect cells + new ImplicitCellDetector().detectCells(g); + new BlockOrganizer().organize(g); + + assertEquals(1, g.getCells().size()); + Cell cell = g.getCells().iterator().next(); + assertEquals(Block.Type.SERIAL, cell.getRootBlock().getType()); + SerialBlock sb = (SerialBlock) cell.getRootBlock(); + assertTrue(sb.isEmbedingNodeType(Node.NodeType.BUS)); + assertTrue(sb.isEmbedingNodeType(Node.NodeType.FEEDER)); + assertTrue(sb.getLowerBlock().isEmbedingNodeType(Node.NodeType.BUS)); + assertTrue(sb.getUpperBlock().isEmbedingNodeType(Node.NodeType.FEEDER)); + assertTrue(sb.getSubBlocks().get(0).isEmbedingNodeType(Node.NodeType.BUS)); + assertTrue(sb.getSubBlocks().get(1).isEmbedingNodeType(Node.NodeType.SWITCH)); + + assertSame(Block.Type.PRIMARY, sb.getLowerBlock().getType()); + LegPrimaryBlock subSB = (LegPrimaryBlock) sb.getLowerBlock(); + assertSame(Block.Type.PARALLEL, sb.getUpperBlock().getType()); + BodyParallelBlock subPB = (BodyParallelBlock) sb.getUpperBlock(); + + assertEquals("bbs", sb.getStartingNode().getId()); + assertEquals("bbs", subSB.getStartingNode().getId()); + assertEquals("FICT_vl_2", subPB.getSubBlocks().get(0).getStartingNode().getId()); + + sb.sizing(); + assertEquals(0, sb.getPosition().getH()); + assertEquals(0, sb.getPosition().getV()); + assertEquals(2, sb.getPosition().getHSpan()); + assertEquals(4, sb.getPosition().getVSpan()); + + assertEquals(0, subSB.getPosition().getH()); + assertEquals(0, subSB.getPosition().getV()); + assertEquals(1, subSB.getPosition().getHSpan()); + assertEquals(0, subSB.getPosition().getVSpan()); + + assertEquals(0, subPB.getSubBlocks().get(0).getPosition().getH()); + assertEquals(0, subPB.getSubBlocks().get(0).getPosition().getV()); + assertEquals(1, subPB.getSubBlocks().get(0).getPosition().getHSpan()); + assertEquals(2, subPB.getSubBlocks().get(0).getPosition().getVSpan()); + + assertEquals(1, subPB.getSubBlocks().get(1).getPosition().getH()); + assertEquals(0, subPB.getSubBlocks().get(1).getPosition().getV()); + assertEquals(1, subPB.getSubBlocks().get(1).getPosition().getHSpan()); + assertEquals(2, subPB.getSubBlocks().get(1).getPosition().getVSpan()); + + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50); + + sb.setX(10); + sb.setY(20); + sb.setXSpan(100); + sb.setYSpan(200); + sb.coordHorizontalCase(layoutParameters); + + assertEquals(10, sb.getCoord().getX(), 0); + assertEquals(20, sb.getCoord().getY(), 0); + assertEquals(100, sb.getCoord().getXSpan(), 0); + assertEquals(200, sb.getCoord().getYSpan(), 0); + + assertEquals(-15, subSB.getCoord().getX(), 0); + assertEquals(20, subSB.getCoord().getY(), 0); + assertEquals(50, subSB.getCoord().getXSpan(), 0); + assertEquals(200, subSB.getCoord().getYSpan(), 0); + + assertEquals(-15, subPB.getSubBlocks().get(0).getCoord().getX(), 0); + assertEquals(20, subPB.getSubBlocks().get(0).getCoord().getY(), 0); + assertEquals(50, subPB.getSubBlocks().get(0).getCoord().getXSpan(), 0); + assertEquals(200, subPB.getSubBlocks().get(0).getCoord().getYSpan(), 0); + + assertEquals(35, subPB.getSubBlocks().get(1).getCoord().getX(), 0); + assertEquals(20, subPB.getSubBlocks().get(1).getCoord().getY(), 0); + assertEquals(50, subPB.getSubBlocks().get(1).getCoord().getXSpan(), 0); + assertEquals(200, subPB.getSubBlocks().get(1).getCoord().getYSpan(), 0); + + sb.reverseBlock(); + + assertEquals("FICT_vl_daFictif", sb.getEndingNode().getId()); + assertEquals("FICT_vl_daFictif", subSB.getEndingNode().getId()); + assertEquals("FICT_vl_2", subPB.getSubBlocks().get(1).getEndingNode().getId()); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestShiftFeedersPosition.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestShiftFeedersPosition.java new file mode 100644 index 000000000..5fca39e7b --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestShiftFeedersPosition.java @@ -0,0 +1,199 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.*; +import com.powsybl.sld.model.Graph; +import com.powsybl.sld.model.SubstationGraph; +import com.powsybl.sld.svg.DefaultSubstationDiagramStyleProvider; +import org.junit.Before; +import org.junit.Test; + +/** + * @author Christian Biasuzzi + */ +public class TestShiftFeedersPosition extends AbstractTestCase { + + @Before + public void setUp() { + network = Network.create("testCase11", "test"); + + substation = createSubstation(network, "subst", "subst", Country.FR); + + VoltageLevel vl1 = createVoltageLevel(substation, "vl1", "vl1", TopologyKind.NODE_BREAKER, 400, 50); + + createBusBarSection(vl1, "bbs1", "bbs1", 0, 1, 1); + createBusBarSection(vl1, "bbs2", "bbs2", 1, 1, 2); + createBusBarSection(vl1, "bbs3", "bbs3", 2, 2, 1); + createBusBarSection(vl1, "bbs4", "bbs4", 3, 2, 2); + + createSwitch(vl1, "dsect11", "dsect11", SwitchKind.DISCONNECTOR, false, false, true, 0, 14); + createSwitch(vl1, "dtrct11", "dtrct11", SwitchKind.BREAKER, true, false, true, 14, 15); + createSwitch(vl1, "dsect12", "dsect12", SwitchKind.DISCONNECTOR, false, false, true, 15, 1); + + createSwitch(vl1, "dsect21", "dsect21", SwitchKind.DISCONNECTOR, false, false, true, 2, 16); + createSwitch(vl1, "dtrct21", "dtrct21", SwitchKind.BREAKER, true, false, true, 16, 17); + createSwitch(vl1, "dsect22", "dsect22", SwitchKind.DISCONNECTOR, false, false, true, 17, 3); + + createLoad(vl1, "load1", "load1", "load1", 0, ConnectablePosition.Direction.TOP, 4, 10, 10); + createSwitch(vl1, "dload1", "dload1", SwitchKind.DISCONNECTOR, false, false, true, 0, 5); + createSwitch(vl1, "bload1", "bload1", SwitchKind.BREAKER, true, false, true, 4, 5); + + createGenerator(vl1, "gen1", "gen1", "gen1", 1, ConnectablePosition.Direction.BOTTOM, 6, 0, 20, false, 10, 10); + createSwitch(vl1, "dgen1", "dgen1", SwitchKind.DISCONNECTOR, false, false, true, 2, 7); + createSwitch(vl1, "bgen1", "bgen1", SwitchKind.BREAKER, true, false, true, 6, 7); + + createLoad(vl1, "load2", "load2", "load2", 2, ConnectablePosition.Direction.TOP, 8, 10, 10); + createSwitch(vl1, "dload2", "dload2", SwitchKind.DISCONNECTOR, false, false, true, 1, 9); + createSwitch(vl1, "bload2", "bload2", SwitchKind.BREAKER, true, false, true, 8, 9); + + createGenerator(vl1, "gen2", "gen2", "gen2", 3, ConnectablePosition.Direction.BOTTOM, 10, 0, 20, false, 10, 10); + createSwitch(vl1, "dgen2", "dgen2", SwitchKind.DISCONNECTOR, false, false, true, 3, 11); + createSwitch(vl1, "bgen2", "bgen2", SwitchKind.BREAKER, true, false, true, 10, 11); + + } + + private static Substation createSubstation(Network n, String id, String name, Country country) { + Substation s = n.newSubstation() + .setId(id) + .setName(name) + .setCountry(country) + .add(); + return s; + } + + private static VoltageLevel createVoltageLevel(Substation s, String id, String name, + TopologyKind topology, double vNom, int nodeCount) { + VoltageLevel vl = s.newVoltageLevel() + .setId(id) + .setName(name) + .setTopologyKind(topology) + .setNominalV(vNom) + .add(); + vl.getNodeBreakerView() + .setNodeCount(nodeCount); + return vl; + } + + private static void createSwitch(VoltageLevel vl, String id, String name, SwitchKind kind, boolean retained, boolean open, boolean fictitious, int node1, int node2) { + vl.getNodeBreakerView().newSwitch() + .setId(id) + .setName(name) + .setKind(kind) + .setRetained(retained) + .setOpen(open) + .setFictitious(fictitious) + .setNode1(node1) + .setNode2(node2) + .add(); + } + + private static void createBusBarSection(VoltageLevel vl, String id, String name, int node, int busbarIndex, int sectionIndex) { + BusbarSection bbs = vl.getNodeBreakerView().newBusbarSection() + .setId(id) + .setName(name) + .setNode(node) + .add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, busbarIndex, sectionIndex)); + } + + private static void createLoad(VoltageLevel vl, String id, String name, String feederName, int feederOrder, + ConnectablePosition.Direction direction, int node, double p0, double q0) { + Load load = vl.newLoad() + .setId(id) + .setName(name) + .setNode(node) + .setP0(p0) + .setQ0(q0) + .add(); + load.addExtension(ConnectablePosition.class, new ConnectablePosition<>(load, new ConnectablePosition + .Feeder(feederName, feederOrder, direction), null, null, null)); + } + + private static void createGenerator(VoltageLevel vl, String id, String name, String feederName, int feederOrder, + ConnectablePosition.Direction direction, int node, + double minP, double maxP, boolean voltageRegulator, + double targetP, double targetQ) { + Generator gen = vl.newGenerator() + .setId(id) + .setName(name) + .setNode(node) + .setMinP(minP) + .setMaxP(maxP) + .setVoltageRegulatorOn(voltageRegulator) + .setTargetP(targetP) + .setTargetQ(targetQ) + .add(); + gen.addExtension(ConnectablePosition.class, new ConnectablePosition<>(gen, new ConnectablePosition + .Feeder(feederName, feederOrder, direction), null, null, null)); + } + + @Test + public void test() { + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(20) + .setTranslateY(50) + .setInitialXBus(0) + .setInitialYBus(260) + .setVerticalSpaceBus(25) + .setHorizontalBusPadding(20) + .setCellWidth(50) + .setExternCellHeight(250) + .setInternCellHeight(40) + .setStackHeight(30) + .setShowGrid(true) + .setShowInternalNodes(false) + .setScaleFactor(1) + .setHorizontalSubstationPadding(50) + .setVerticalSubstationPadding(50) + .setDrawStraightWires(false) + .setHorizontalSnakeLinePadding(30) + .setVerticalSnakeLinePadding(30) + .setShowInductorFor3WT(false); + + // build substation graph + SubstationGraph g = SubstationGraph.create(substation); + + // write SVG and compare to reference (horizontal layout and defaut style provider) + new HorizontalSubstationLayoutFactory().create(g, new PositionVoltageLevelLayoutFactory()).run(layoutParameters); + compareSvg(g, layoutParameters, "/TestDefaultFeedersPosition.svg", new DefaultSubstationDiagramStyleProvider()); + + // re-build substation graph, write SVG using the shifted feeders positioner and compare to reference (same layout providers) + g = SubstationGraph.create(substation); + new HorizontalSubstationLayoutFactory().create(g, new PositionVoltageLevelLayoutFactory()).run(layoutParameters); + compareSvg(g, layoutParameters.setShiftFeedersPosition(true), "/TestShiftFeedersPosition.svg", new DefaultSubstationDiagramStyleProvider()); + } + + @Test + public void test2() { + LayoutParameters layoutParameters = new LayoutParameters(); + VoltageLevelLayoutFactory fakeVoltageLevelLayoutFactory = new VoltageLevelLayoutFactory() { + @Override + public VoltageLevelLayout create(Graph graph) { + return new VoltageLevelLayout() { + @Override + public void run(LayoutParameters layoutParam) { + } + }; + } + }; + + // build substation graph + SubstationGraph g = SubstationGraph.create(substation); + new HorizontalSubstationLayoutFactory().create(g, fakeVoltageLevelLayoutFactory).run(layoutParameters); + // write SVG and compare to reference (with a fake VL layout) + compareSvg(g, layoutParameters, "/TestDefaultFeedersPosition2.svg", new DefaultSubstationDiagramStyleProvider()); + + // re-build substation graph, write SVG using the shifted feeders positioner (same output as before expected) + g = SubstationGraph.create(substation); + new HorizontalSubstationLayoutFactory().create(g, fakeVoltageLevelLayoutFactory).run(layoutParameters); + compareSvg(g, layoutParameters.setShiftFeedersPosition(true), "/TestDefaultFeedersPosition2.svg", new DefaultSubstationDiagramStyleProvider()); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestSubstationDiagram.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestSubstationDiagram.java new file mode 100644 index 000000000..d601592ef --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestSubstationDiagram.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.google.common.io.ByteStreams; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.*; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import com.powsybl.sld.svg.DefaultSVGWriter; +import com.powsybl.sld.svg.SVGWriter; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.Assert.assertEquals; + +/** + *
+ * l
+ * |
+ * b
+ * |
+ * d
+ * |
+ * ------ bbs
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Giovanni Ferrari + * @author Franck Lecuyer + */ +public class TestSubstationDiagram extends AbstractTestCase { + + private FileSystem fileSystem; + private Path tmpDir; + + @Before + public void setUp() throws IOException { + network = Network.create("testCase1", "test"); + substation = network.newSubstation().setId("s").setCountry(Country.FR).add(); + vl = substation.newVoltageLevel().setId("vl").setTopologyKind(TopologyKind.NODE_BREAKER).setNominalV(400).add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView().setNodeCount(10); + BusbarSection bbs = view.newBusbarSection().setId("bbs").setNode(0).add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + Load l = vl.newLoad().setId("l").setNode(2).setP0(10).setQ0(10).add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, + new ConnectablePosition.Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newDisconnector().setId("d").setNode1(0).setNode2(1).add(); + view.newBreaker().setId("b").setNode1(1).setNode2(2).add(); + + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + tmpDir = Files.createDirectory(fileSystem.getPath("/tmp")); + + } + + @Test + public void test() throws IOException { + ComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + LayoutParameters layoutParameters = new LayoutParameters(); + VoltageLevelLayoutFactory voltageLevelLayoutFactory = new PositionVoltageLevelLayoutFactory(); + SubstationLayoutFactory substationLayoutFactory = new HorizontalSubstationLayoutFactory(); + Path outSvg = tmpDir.resolve("sub.svg"); + + SubstationDiagram.build(substation).writeSvg("", componentLibrary, layoutParameters, network, outSvg); + String svgStr = normalizeLineSeparator(new String(Files.readAllBytes(outSvg), StandardCharsets.UTF_8)); + +// FileWriter fw = new FileWriter(System.getProperty("user.home") + "/TestSubstation.svg"); +// fw.write(svgStr); +// fw.close(); + + String refSvg = normalizeLineSeparator( + new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/TestSubstation.svg")), + StandardCharsets.UTF_8)); + assertEquals(refSvg, svgStr); + } + + @Test + public void testWriter() throws IOException { + ComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + LayoutParameters layoutParameters = new LayoutParameters(); + + SVGWriter writer = new DefaultSVGWriter(componentLibrary, layoutParameters); + + Path outSvg = tmpDir.resolve("sub.svg"); + + SubstationDiagram.build(substation).writeSvg("", writer, network, outSvg); + String svgStr = normalizeLineSeparator(new String(Files.readAllBytes(outSvg), StandardCharsets.UTF_8)); + + String refSvg = normalizeLineSeparator( + new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/TestSubstation.svg")), + StandardCharsets.UTF_8)); + assertEquals(refSvg, svgStr); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestUnicityNodeIdWithMutipleNetwork.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestUnicityNodeIdWithMutipleNetwork.java new file mode 100644 index 000000000..2fca6414d --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestUnicityNodeIdWithMutipleNetwork.java @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.google.common.io.ByteStreams; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayoutFactory; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +/** + * @author Franck Lecuyer + */ +public class TestUnicityNodeIdWithMutipleNetwork extends AbstractTestCase { + + private FileSystem fileSystem; + private Path tmpDir; + + private Network network2; + private Substation substation2; + private VoltageLevel vl2; + + @Before + public void setUp() throws IOException { + // Create first network with a substation and a voltageLevel + network = Network.create("n1", "test"); + substation = network.newSubstation().setId("s").setCountry(Country.FR).add(); + vl = substation.newVoltageLevel().setId("vl").setTopologyKind(TopologyKind.NODE_BREAKER).setNominalV(400).add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView().setNodeCount(10); + BusbarSection bbs = view.newBusbarSection().setId("bbs").setNode(0).add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + Load l = vl.newLoad().setId("l").setNode(2).setP0(10).setQ0(10).add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, + new ConnectablePosition.Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newDisconnector().setId("d").setNode1(0).setNode2(1).add(); + view.newBreaker().setId("b").setNode1(1).setNode2(2).add(); + + // Create second network with a substation and a voltageLevel + network2 = Network.create("n2", "test"); + substation2 = network2.newSubstation().setId("s").setCountry(Country.FR).add(); + vl2 = substation2.newVoltageLevel().setId("vl").setTopologyKind(TopologyKind.NODE_BREAKER).setNominalV(400).add(); + VoltageLevel.NodeBreakerView view2 = vl2.getNodeBreakerView().setNodeCount(10); + BusbarSection bbs2 = view2.newBusbarSection().setId("bbs").setNode(0).add(); + bbs2.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs2, 1, 1)); + Load l2 = vl2.newLoad().setId("l").setNode(2).setP0(10).setQ0(10).add(); + l2.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l2, + new ConnectablePosition.Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view2.newDisconnector().setId("d").setNode1(0).setNode2(1).add(); + view2.newBreaker().setId("b").setNode1(1).setNode2(2).add(); + + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + tmpDir = Files.createDirectory(fileSystem.getPath("/tmp")); + } + + @Test + public void test() throws IOException { + ComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + LayoutParameters layoutParameters = new LayoutParameters(); + + // Generating svg for voltage level in first network + Path outSvg1 = tmpDir.resolve("vl_network1.svg"); + VoltageLevelDiagram.build(vl, new PositionVoltageLevelLayoutFactory(), false, false).writeSvg("network1_", componentLibrary, layoutParameters, network, outSvg1); + String svgStr1 = normalizeLineSeparator(new String(Files.readAllBytes(outSvg1), StandardCharsets.UTF_8)); + +// FileWriter fw1 = new FileWriter(System.getProperty("user.home") + "/TestUnicityNodeIdNetWork1.svg"); +// fw1.write(svgStr1); +// fw1.close(); + + String refSvg1 = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/TestUnicityNodeIdNetWork1.svg")), StandardCharsets.UTF_8)); + assertEquals(refSvg1, svgStr1); + + // Generating svg for voltage level in second network + Path outSvg2 = tmpDir.resolve("vl_network2.svg"); + VoltageLevelDiagram.build(vl2, new PositionVoltageLevelLayoutFactory(), false, false).writeSvg("network2_", componentLibrary, layoutParameters, network2, outSvg2); + String svgStr2 = normalizeLineSeparator(new String(Files.readAllBytes(outSvg2), StandardCharsets.UTF_8)); + +// FileWriter fw2 = new FileWriter(System.getProperty("user.home") + "/TestUnicityNodeIdNetWork2.svg"); +// fw2.write(svgStr2); +// fw2.close(); + + String refSvg2 = normalizeLineSeparator(new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/TestUnicityNodeIdNetWork2.svg")), StandardCharsets.UTF_8)); + assertEquals(refSvg2, svgStr2); + + assertNotEquals(svgStr1, svgStr2); + + // Generating the svg for the voltage levels, without the prefix identifying each network + VoltageLevelDiagram.build(vl, new PositionVoltageLevelLayoutFactory(), false, false).writeSvg("", componentLibrary, layoutParameters, network, outSvg1); + svgStr1 = normalizeLineSeparator(new String(Files.readAllBytes(outSvg1), StandardCharsets.UTF_8)); + + VoltageLevelDiagram.build(vl2, new PositionVoltageLevelLayoutFactory(), false, false).writeSvg("", componentLibrary, layoutParameters, network2, outSvg2); + svgStr2 = normalizeLineSeparator(new String(Files.readAllBytes(outSvg2), StandardCharsets.UTF_8)); + + assertEquals(svgStr1, svgStr2); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/TestVoltageLevelDiagram.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestVoltageLevelDiagram.java new file mode 100644 index 000000000..9f23d647e --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/TestVoltageLevelDiagram.java @@ -0,0 +1,107 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld; + +import com.google.common.io.ByteStreams; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.PositionVoltageLevelLayoutFactory; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import com.powsybl.sld.svg.DefaultSVGWriter; +import com.powsybl.sld.svg.SVGWriter; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.Assert.assertEquals; + +/** + *
+ * l
+ * |
+ * b
+ * |
+ * d
+ * |
+ * ------ bbs
+ * 
+ * + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Giovanni Ferrari + * @author Franck Lecuyer + */ +public class TestVoltageLevelDiagram extends AbstractTestCase { + + private FileSystem fileSystem; + private Path tmpDir; + + @Before + public void setUp() throws IOException { + network = Network.create("testCase1", "test"); + substation = network.newSubstation().setId("s").setCountry(Country.FR).add(); + vl = substation.newVoltageLevel().setId("vl").setTopologyKind(TopologyKind.NODE_BREAKER).setNominalV(400).add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView().setNodeCount(10); + BusbarSection bbs = view.newBusbarSection().setId("bbs").setNode(0).add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + Load l = vl.newLoad().setId("l").setNode(2).setP0(10).setQ0(10).add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, + new ConnectablePosition.Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newDisconnector().setId("d").setNode1(0).setNode2(1).add(); + view.newBreaker().setId("b").setNode1(1).setNode2(2).add(); + + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + tmpDir = Files.createDirectory(fileSystem.getPath("/tmp")); + } + + @Test + public void test() throws IOException { + ComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + LayoutParameters layoutParameters = new LayoutParameters(); + + Path outSvg = tmpDir.resolve("vl.svg"); + + VoltageLevelDiagram.build(vl, new PositionVoltageLevelLayoutFactory(), false, false).writeSvg("", componentLibrary, layoutParameters, network, outSvg); + String svgStr = normalizeLineSeparator(new String(Files.readAllBytes(outSvg), StandardCharsets.UTF_8)); + +// FileWriter fw = new FileWriter(System.getProperty("user.home") + "/TestVL.svg"); +// fw.write(svgStr); +// fw.close(); + + String refSvg = normalizeLineSeparator(new String( + ByteStreams.toByteArray(getClass().getResourceAsStream("/TestVL.svg")), StandardCharsets.UTF_8)); + assertEquals(refSvg, svgStr); + } + + @Test + public void testWriter() throws IOException { + ComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + LayoutParameters layoutParameters = new LayoutParameters(); + + SVGWriter writer = new DefaultSVGWriter(componentLibrary, layoutParameters); + + Path outSvg = tmpDir.resolve("vl.svg"); + + VoltageLevelDiagram.build(vl, new PositionVoltageLevelLayoutFactory(), false, false).writeSvg("", writer, network, outSvg); + String svgStr = normalizeLineSeparator(new String(Files.readAllBytes(outSvg), StandardCharsets.UTF_8)); + + String refSvg = normalizeLineSeparator(new String( + ByteStreams.toByteArray(getClass().getResourceAsStream("/TestVL.svg")), StandardCharsets.UTF_8)); + assertEquals(refSvg, svgStr); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/layout/LayoutParametersTest.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/layout/LayoutParametersTest.java new file mode 100644 index 000000000..c08b6b8ae --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/layout/LayoutParametersTest.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.layout; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * @author Giovanni Ferrari + */ +public class LayoutParametersTest { + + @Test + public void test() { + LayoutParameters layoutParameters = new LayoutParameters() + .setTranslateX(25) + .setTranslateY(60) + .setInitialXBus(10) + .setInitialYBus(280) + .setVerticalSpaceBus(20) + .setHorizontalBusPadding(30) + .setCellWidth(70) + .setExternCellHeight(240) + .setInternCellHeight(50) + .setStackHeight(40) + .setShowGrid(true) + .setShowInternalNodes(true) + .setScaleFactor(2) + .setHorizontalSubstationPadding(60) + .setVerticalSubstationPadding(70) + .setDrawStraightWires(true) + .setHorizontalSnakeLinePadding(25) + .setVerticalSnakeLinePadding(40) + .setArrowDistance(25) + .setShowInductorFor3WT(true) + .setDiagramName("diag") + .setShiftFeedersPosition(false) + .setScaleShiftFeedersPosition(2) + .setAvoidSVGComponentsDuplication(true); + LayoutParameters layoutParameters2 = new LayoutParameters(layoutParameters); + + assertEquals(layoutParameters.getTranslateX(), layoutParameters2.getTranslateX(), 0); + assertEquals(layoutParameters.getTranslateY(), layoutParameters2.getTranslateY(), 0); + assertEquals(layoutParameters.getInitialXBus(), layoutParameters2.getInitialXBus(), 0); + assertEquals(layoutParameters.getInitialYBus(), layoutParameters2.getInitialYBus(), 0); + assertEquals(layoutParameters.getVerticalSpaceBus(), layoutParameters2.getVerticalSpaceBus(), 0); + assertEquals(layoutParameters.getHorizontalBusPadding(), layoutParameters2.getHorizontalBusPadding(), 0); + assertEquals(layoutParameters.getCellWidth(), layoutParameters2.getCellWidth(), 0); + assertEquals(layoutParameters.getExternCellHeight(), layoutParameters2.getExternCellHeight(), 0); + assertEquals(layoutParameters.getInternCellHeight(), layoutParameters2.getInternCellHeight(), 0); + assertEquals(layoutParameters.getStackHeight(), layoutParameters2.getStackHeight(), 0); + assertEquals(layoutParameters.isShowGrid(), layoutParameters2.isShowGrid()); + assertEquals(layoutParameters.isShowInternalNodes(), layoutParameters2.isShowInternalNodes()); + assertEquals(layoutParameters.getScaleFactor(), layoutParameters2.getScaleFactor(), 0); + assertEquals(layoutParameters.getHorizontalSubstationPadding(), layoutParameters2.getHorizontalSubstationPadding(), 0); + assertEquals(layoutParameters.getVerticalSubstationPadding(), layoutParameters2.getVerticalSubstationPadding(), 0); + assertEquals(layoutParameters.isDrawStraightWires(), layoutParameters2.isDrawStraightWires()); + assertEquals(layoutParameters.getHorizontalSnakeLinePadding(), layoutParameters2.getHorizontalSnakeLinePadding(), 0); + assertEquals(layoutParameters.getVerticalSnakeLinePadding(), layoutParameters2.getVerticalSnakeLinePadding(), 0); + assertEquals(layoutParameters.getArrowDistance(), layoutParameters2.getArrowDistance(), 0); + assertEquals(layoutParameters.isShiftFeedersPosition(), layoutParameters2.isShiftFeedersPosition()); + assertEquals(layoutParameters.getScaleShiftFeedersPosition(), layoutParameters2.getScaleShiftFeedersPosition(), 0); + assertEquals(layoutParameters.isShowInductorFor3WT(), layoutParameters2.isShowInductorFor3WT()); + assertEquals(layoutParameters.getDiagramName(), layoutParameters2.getDiagramName()); + assertEquals(layoutParameters.isAvoidSVGComponentsDuplication(), layoutParameters2.isAvoidSVGComponentsDuplication()); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/library/AnchorPointTest.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/library/AnchorPointTest.java new file mode 100644 index 000000000..4b3dc934a --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/library/AnchorPointTest.java @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class AnchorPointTest { + + @Test + public void rotationTest() { + AnchorPoint anchorPoint1 = new AnchorPoint(0, -10, AnchorOrientation.VERTICAL); + AnchorPoint anchorPoint2 = new AnchorPoint(0, 10, AnchorOrientation.VERTICAL); + AnchorPoint rotatedAnchorPoint1 = anchorPoint1.rotate(); + assertEquals(AnchorOrientation.HORIZONTAL, rotatedAnchorPoint1.getOrientation()); + assertEquals(-10, rotatedAnchorPoint1.getX(), 0); + assertEquals(0, rotatedAnchorPoint1.getY(), 0); + AnchorPoint rotatedAnchorPoint2 = anchorPoint2.rotate(); + assertEquals(AnchorOrientation.HORIZONTAL, rotatedAnchorPoint2.getOrientation()); + assertEquals(10, rotatedAnchorPoint2.getX(), 0); + assertEquals(0, rotatedAnchorPoint2.getY(), 0); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/library/ComponentsTest.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/library/ComponentsTest.java new file mode 100644 index 000000000..2dcabcad8 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/library/ComponentsTest.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.library; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.charset.StandardCharsets; + +import static com.powsybl.sld.library.ComponentTypeName.BREAKER; +import static org.junit.Assert.assertEquals; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class ComponentsTest { + + @Test + public void test() { + String componentXml = String.join(System.lineSeparator(), + "", + "", + " ", + " breaker.svg", + " ", + " ", + " ", + " ", + " ", + " ", + ""); + Components components; + try (ByteArrayInputStream is = new ByteArrayInputStream(componentXml.getBytes(StandardCharsets.UTF_8))) { + components = Components.load(is); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + assertEquals(1, components.getComponents().size()); + assertEquals("breaker.svg", components.getComponents().get(0).getFileName()); + assertEquals(BREAKER, components.getComponents().get(0).getMetadata().getType()); + assertEquals(18, components.getComponents().get(0).getMetadata().getSize().getWidth(), 0); + assertEquals(19, components.getComponents().get(0).getMetadata().getSize().getHeight(), 0); + assertEquals(2, components.getComponents().get(0).getMetadata().getAnchorPoints().size()); + assertEquals(9, components.getComponents().get(0).getMetadata().getAnchorPoints().get(0).getX(), 0); + assertEquals(0, components.getComponents().get(0).getMetadata().getAnchorPoints().get(0).getY(), 0); + assertEquals(AnchorOrientation.VERTICAL, components.getComponents().get(0).getMetadata().getAnchorPoints().get(0).getOrientation()); + assertEquals(9, components.getComponents().get(0).getMetadata().getAnchorPoints().get(1).getX(), 0); + assertEquals(18, components.getComponents().get(0).getMetadata().getAnchorPoints().get(1).getY(), 0); + assertEquals(AnchorOrientation.HORIZONTAL, components.getComponents().get(0).getMetadata().getAnchorPoints().get(1).getOrientation()); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/GraphMetadataTest.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/GraphMetadataTest.java new file mode 100644 index 000000000..7848368a0 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/GraphMetadataTest.java @@ -0,0 +1,122 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableList; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.commons.json.JsonUtil; +import com.powsybl.sld.library.*; +import com.powsybl.sld.model.BusCell; +import com.powsybl.sld.svg.GraphMetadata.ArrowMetadata; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static com.powsybl.sld.library.ComponentTypeName.BREAKER; +import static com.powsybl.sld.library.ComponentTypeName.BUSBAR_SECTION; +import static org.junit.Assert.*; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class GraphMetadataTest { + + private FileSystem fileSystem; + private Path tmpDir; + + @Before + public void setUp() throws IOException { + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + tmpDir = Files.createDirectory(fileSystem.getPath("/tmp")); + } + + @Test + public void test() throws IOException { + GraphMetadata metadata = new GraphMetadata(); + metadata.addComponentMetadata(new ComponentMetadata(BREAKER, + "br1", + ImmutableList.of(new AnchorPoint(5, 4, AnchorOrientation.NONE)), + new ComponentSize(10, 12))); + + metadata.addNodeMetadata(new GraphMetadata.NodeMetadata("id1", "vid1", null, BREAKER, null, false, BusCell.Direction.UNDEFINED, false)); + metadata.addNodeMetadata(new GraphMetadata.NodeMetadata("id2", "vid2", null, BUSBAR_SECTION, null, false, BusCell.Direction.UNDEFINED, false)); + metadata.addWireMetadata(new GraphMetadata.WireMetadata("id3", "id1", "id2", false, false)); + metadata.addArrowMetadata(new ArrowMetadata("id1", "id3", 20)); + + ObjectMapper objectMapper = JsonUtil.createObjectMapper(); + String json = objectMapper.writerWithDefaultPrettyPrinter() + .writeValueAsString(metadata); + + GraphMetadata metadata2 = objectMapper.readValue(json, GraphMetadata.class); + assertEquals(1, metadata2.getComponentMetadata().size()); + assertNotNull(metadata2.getComponentMetadata(BREAKER)); + Assert.assertEquals(1, metadata2.getComponentMetadata(BREAKER).getAnchorPoints().size()); + Assert.assertEquals(5, metadata2.getComponentMetadata(BREAKER).getAnchorPoints().get(0).getX(), 0); + Assert.assertEquals(4, metadata2.getComponentMetadata(BREAKER).getAnchorPoints().get(0).getY(), 0); + Assert.assertEquals(AnchorOrientation.NONE, metadata2.getComponentMetadata(BREAKER).getAnchorPoints().get(0).getOrientation()); + Assert.assertEquals(10, metadata2.getComponentMetadata(BREAKER).getSize().getWidth(), 0); + Assert.assertEquals(12, metadata2.getComponentMetadata(BREAKER).getSize().getHeight(), 0); + assertEquals(2, metadata2.getNodeMetadata().size()); + assertNotNull(metadata2.getNodeMetadata("id1")); + assertEquals("id1", metadata2.getNodeMetadata("id1").getId()); + assertEquals("vid1", metadata2.getNodeMetadata("id1").getVId()); + Assert.assertEquals(BREAKER, metadata2.getNodeMetadata("id1").getComponentType()); + Assert.assertEquals(BUSBAR_SECTION, metadata2.getNodeMetadata("id2").getComponentType()); + assertEquals("id2", metadata2.getNodeMetadata("id2").getId()); + assertEquals("vid2", metadata2.getNodeMetadata("id2").getVId()); + assertNotNull(metadata2.getNodeMetadata("id2")); + assertEquals(1, metadata2.getWireMetadata().size()); + assertNotNull(metadata2.getWireMetadata("id3")); + assertEquals("id3", metadata2.getWireMetadata("id3").getId()); + assertEquals("id1", metadata2.getWireMetadata("id3").getNodeId1()); + assertEquals("id2", metadata2.getWireMetadata("id3").getNodeId2()); + assertFalse(metadata2.getWireMetadata("id3").isStraight()); + assertEquals("id3", metadata2.getArrowMetadata("id1").getWireId()); + assertEquals(AnchorOrientation.NONE, metadata2.getAnchorPoints(BREAKER, "br1").get(0).getOrientation()); + assertEquals(5, metadata2.getAnchorPoints(BREAKER, "br1").get(0).getX(), 0); + assertEquals(4, metadata2.getAnchorPoints(BREAKER, "br1").get(0).getY(), 0); + + Path meta = tmpDir.resolve("meta.json"); + metadata.writeJson(meta); + GraphMetadata metadata3 = GraphMetadata.parseJson(meta); + assertEquals(1, metadata3.getComponentMetadata().size()); + assertNotNull(metadata3.getComponentMetadata(BREAKER)); + Assert.assertEquals(1, metadata3.getComponentMetadata(BREAKER).getAnchorPoints().size()); + Assert.assertEquals(5, metadata3.getComponentMetadata(BREAKER).getAnchorPoints().get(0).getX(), 0); + Assert.assertEquals(4, metadata3.getComponentMetadata(BREAKER).getAnchorPoints().get(0).getY(), 0); + Assert.assertEquals(AnchorOrientation.NONE, metadata3.getComponentMetadata(BREAKER).getAnchorPoints().get(0).getOrientation()); + Assert.assertEquals(10, metadata3.getComponentMetadata(BREAKER).getSize().getWidth(), 0); + Assert.assertEquals(12, metadata3.getComponentMetadata(BREAKER).getSize().getHeight(), 0); + assertEquals(2, metadata3.getNodeMetadata().size()); + assertNotNull(metadata3.getNodeMetadata("id1")); + assertEquals("id1", metadata3.getNodeMetadata("id1").getId()); + assertEquals("vid1", metadata3.getNodeMetadata("id1").getVId()); + Assert.assertEquals(BREAKER, metadata3.getNodeMetadata("id1").getComponentType()); + Assert.assertEquals(BUSBAR_SECTION, metadata3.getNodeMetadata("id2").getComponentType()); + assertEquals("id2", metadata3.getNodeMetadata("id2").getId()); + assertEquals("vid2", metadata3.getNodeMetadata("id2").getVId()); + assertNotNull(metadata3.getNodeMetadata("id2")); + assertEquals(1, metadata3.getWireMetadata().size()); + assertNotNull(metadata3.getWireMetadata("id3")); + assertEquals("id3", metadata3.getWireMetadata("id3").getId()); + assertEquals("id1", metadata3.getWireMetadata("id3").getNodeId1()); + assertEquals("id2", metadata3.getWireMetadata("id3").getNodeId2()); + assertFalse(metadata3.getWireMetadata("id3").isStraight()); + assertEquals("id3", metadata3.getArrowMetadata("id1").getWireId()); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/InitialValueProviderTest.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/InitialValueProviderTest.java new file mode 100644 index 000000000..4c7bd7d63 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/InitialValueProviderTest.java @@ -0,0 +1,110 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.network.StaticVarCompensator.RegulationMode; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.model.Graph; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Giovanni Ferrari + */ +public class InitialValueProviderTest { + + private Network network; + private Substation substation; + private VoltageLevel vl; + + @Before + public void setUp() { + network = Network.create("testCase1", "test"); + substation = network.newSubstation().setId("s").setCountry(Country.FR).add(); + vl = substation.newVoltageLevel().setId("vl").setTopologyKind(TopologyKind.NODE_BREAKER).setNominalV(400).add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView().setNodeCount(10); + BusbarSection bbs = view.newBusbarSection().setId("bbs").setNode(0).add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + BusbarSection bbs2 = view.newBusbarSection().setId("bbs2").setNode(3).add(); + bbs2.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs2, 2, 2)); + vl.newStaticVarCompensator() + .setId("svc") + .setName("svc") + .setNode(2) + .setBmin(0.0002) + .setBmax(0.0008) + .setRegulationMode(RegulationMode.VOLTAGE) + .setVoltageSetPoint(390) + .add(); + vl.newVscConverterStation() + .setId("vsc") + .setName("Converter1") + .setNode(1) + .setLossFactor(0.011f) + .setVoltageSetpoint(405.0) + .setVoltageRegulatorOn(true) + .add(); + vl.newShuntCompensator() + .setId("C1") + .setName("Filter 1") + .setNode(4) + .setbPerSection(1e-5) + .setCurrentSectionCount(1) + .setMaximumSectionCount(1) + .add(); + view.newDisconnector().setId("d").setNode1(0).setNode2(1).add(); + view.newBreaker().setId("b").setNode1(1).setNode2(2).add(); + view.newBreaker().setId("b2").setNode1(3).setNode2(4).add(); + } + + @Test + public void test() { + Network network2 = Network.create("testCase2", "test2"); + DefaultSubstationDiagramInitialValueProvider initProvider = new DefaultSubstationDiagramInitialValueProvider(network2); + Graph g = Graph.create(vl, false, false, false); + InitialValue init = initProvider.getInitialValue(g.getNode("svc")); + assertFalse(init.getLabel1().isPresent()); + assertFalse(init.getLabel2().isPresent()); + assertFalse(init.getLabel3().isPresent()); + assertFalse(init.getLabel4().isPresent()); + assertFalse(init.getArrowDirection1().isPresent()); + assertFalse(init.getArrowDirection2().isPresent()); + DefaultSubstationDiagramInitialValueProvider initProvider1 = new DefaultSubstationDiagramInitialValueProvider(network); + InitialValue init1 = initProvider1.getInitialValue(g.getNode("svc")); + assertTrue(init1.getLabel1().isPresent()); + assertTrue(init1.getLabel2().isPresent()); + assertFalse(init1.getLabel3().isPresent()); + assertFalse(init1.getLabel4().isPresent()); + assertTrue(init1.getArrowDirection1().isPresent()); + assertTrue(init1.getArrowDirection2().isPresent()); + InitialValue init2 = initProvider1.getInitialValue(g.getNode("vsc")); + assertTrue(init2.getLabel1().isPresent()); + assertTrue(init2.getLabel2().isPresent()); + assertFalse(init2.getLabel3().isPresent()); + assertFalse(init2.getLabel4().isPresent()); + assertTrue(init2.getArrowDirection1().isPresent()); + assertTrue(init2.getArrowDirection2().isPresent()); + InitialValue init3 = initProvider1.getInitialValue(g.getNode("C1")); + assertTrue(init3.getLabel1().isPresent()); + assertTrue(init3.getLabel2().isPresent()); + assertFalse(init3.getLabel3().isPresent()); + assertFalse(init3.getLabel4().isPresent()); + assertTrue(init3.getArrowDirection1().isPresent()); + assertTrue(init3.getArrowDirection2().isPresent()); + InitialValue init4 = initProvider1.getInitialValue(g.getNode("b")); + assertFalse(init4.getLabel1().isPresent()); + assertFalse(init4.getLabel2().isPresent()); + assertFalse(init4.getLabel3().isPresent()); + assertFalse(init4.getLabel4().isPresent()); + assertFalse(init4.getArrowDirection1().isPresent()); + assertFalse(init4.getArrowDirection2().isPresent()); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/NominalVoltageStyleTest.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/NominalVoltageStyleTest.java new file mode 100644 index 000000000..7e8ea8fcb --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/NominalVoltageStyleTest.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.google.common.io.ByteStreams; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.SubstationDiagram; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import com.powsybl.sld.util.NominalVoltageSubstationDiagramStyleProvider; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.Assert.assertEquals; + +/** + * @author Giovanni Ferrari + * @author Franck Lecuyer + */ +public class NominalVoltageStyleTest { + + private Network network; + private Substation substation; + private VoltageLevel vl; + private FileSystem fileSystem; + private Path tmpDir; + + @Before + public void setUp() throws IOException { + network = Network.create("testCase1", "test"); + substation = network.newSubstation().setId("s").setCountry(Country.FR).add(); + vl = substation.newVoltageLevel().setId("vl").setTopologyKind(TopologyKind.NODE_BREAKER).setNominalV(400).add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView().setNodeCount(10); + BusbarSection bbs = view.newBusbarSection().setId("bbs").setNode(0).add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + Load l = vl.newLoad().setId("l").setNode(2).setP0(10).setQ0(10).add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, + new ConnectablePosition.Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newDisconnector().setId("d").setNode1(0).setNode2(1).add(); + view.newBreaker().setId("b").setNode1(1).setNode2(2).add(); + + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + tmpDir = Files.createDirectory(fileSystem.getPath("/tmp")); + } + + @Test + public void test() throws IOException { + ComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + LayoutParameters layoutParameters = new LayoutParameters(); + + Path outSvg = tmpDir.resolve("sub.svg"); + Path meta = tmpDir.resolve("meta.json"); + + SubstationDiagram.build(substation).writeSvg("", componentLibrary, + layoutParameters, + new DefaultSubstationDiagramInitialValueProvider(network), + new NominalVoltageSubstationDiagramStyleProvider(), + new DefaultNodeLabelConfiguration(componentLibrary), + Files.newBufferedWriter(outSvg, StandardCharsets.UTF_8), + Files.newBufferedWriter(meta)); + + String svgStr = normalizeLineSeparator(new String(Files.readAllBytes(outSvg), StandardCharsets.UTF_8)); + +// FileWriter fw = new FileWriter(System.getProperty("user.home") + "/nominalVoltage.svg"); +// fw.write(svgStr); +// fw.close(); + + String refSvg = normalizeLineSeparator( + new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/nominalVoltage.svg")), + StandardCharsets.UTF_8)); + assertEquals(refSvg, svgStr); + } + + protected static String normalizeLineSeparator(String str) { + return str.replace("\r\n", "\n") + .replace("\r", "\n"); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/TestRGBColor.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/TestRGBColor.java new file mode 100644 index 000000000..d3e684d81 --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/TestRGBColor.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import static org.junit.Assert.assertEquals; + +import java.util.List; + +import org.junit.Test; + +import com.powsybl.sld.util.RGBColor; + +/** + * @author Giovanni Ferrari + */ +public class TestRGBColor { + + @Test + public void test() { + String red = "#FF0000"; + RGBColor color = RGBColor.parse(red); + assertEquals(red, color.toString()); + assertEquals(new RGBColor(255, 0, 0), color); + assertEquals(255, color.getRed()); + assertEquals(0, color.getGreen()); + assertEquals(0, color.getBlue()); + double factor = 0.7d; + List gradient = color.getColorGradient(3, factor); + assertEquals(3, gradient.size()); + assertEquals(color.getBrighter(factor), gradient.get(0)); + assertEquals("#CB0000", gradient.get(2).toString()); + } +} diff --git a/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/TopologicalStyleTest.java b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/TopologicalStyleTest.java new file mode 100644 index 000000000..1eccb66ea --- /dev/null +++ b/single-line-diagram-core/src/test/java/com/powsybl/sld/svg/TopologicalStyleTest.java @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.svg; + +import com.google.common.io.ByteStreams; +import com.google.common.jimfs.Configuration; +import com.google.common.jimfs.Jimfs; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.SubstationDiagram; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import com.powsybl.sld.util.TopologicalStyleProvider; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.Assert.assertEquals; + +/** + * @author Giovanni Ferrari + * @author Franck Lecuyer + */ +public class TopologicalStyleTest { + + private Network network; + private Substation substation; + private VoltageLevel vl; + private FileSystem fileSystem; + private Path tmpDir; + + @Before + public void setUp() throws IOException { + network = Network.create("testCase1", "test"); + substation = network.newSubstation().setId("s").setCountry(Country.FR).add(); + vl = substation.newVoltageLevel().setId("vl").setTopologyKind(TopologyKind.NODE_BREAKER).setNominalV(400).add(); + VoltageLevel.NodeBreakerView view = vl.getNodeBreakerView().setNodeCount(10); + BusbarSection bbs = view.newBusbarSection().setId("bbs").setNode(0).add(); + bbs.addExtension(BusbarSectionPosition.class, new BusbarSectionPosition(bbs, 1, 1)); + Load l = vl.newLoad().setId("l").setNode(2).setP0(10).setQ0(10).add(); + l.addExtension(ConnectablePosition.class, new ConnectablePosition<>(l, + new ConnectablePosition.Feeder("l", 0, ConnectablePosition.Direction.TOP), null, null, null)); + view.newDisconnector().setId("d").setNode1(0).setNode2(1).add(); + view.newBreaker().setId("b").setNode1(1).setNode2(2).add(); + + fileSystem = Jimfs.newFileSystem(Configuration.unix()); + tmpDir = Files.createDirectory(fileSystem.getPath("/tmp")); + } + + @Test + public void test() throws IOException { + ComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + LayoutParameters layoutParameters = new LayoutParameters(); + + Path outSvg = tmpDir.resolve("sub.svg"); + Path meta = tmpDir.resolve("meta.json"); + + Path config = tmpDir.resolve("base-voltages.yml"); + + Files.copy(getClass().getResourceAsStream("/base-voltages.yml"), config); + + SubstationDiagram.build(substation) + .writeSvg("", componentLibrary, + layoutParameters, + new DefaultSubstationDiagramInitialValueProvider(network), + new TopologicalStyleProvider(config), + new DefaultNodeLabelConfiguration(componentLibrary), + Files.newBufferedWriter(outSvg, StandardCharsets.UTF_8), + Files.newBufferedWriter(meta)); + + String svgStr = normalizeLineSeparator(new String(Files.readAllBytes(outSvg), StandardCharsets.UTF_8)); + +// FileWriter fw = new FileWriter(System.getProperty("user.home") + "/topological.svg"); +// fw.write(svgStr); +// fw.close(); + + String refSvg = normalizeLineSeparator( + new String(ByteStreams.toByteArray(getClass().getResourceAsStream("/topological.svg")), + StandardCharsets.UTF_8)); + assertEquals(refSvg, svgStr); + } + + protected static String normalizeLineSeparator(String str) { + return str.replace("\r\n", "\n") + .replace("\r", "\n"); + } +} diff --git a/single-line-diagram-core/src/test/resources/TestCase1.svg b/single-line-diagram-core/src/test/resources/TestCase1.svg new file mode 100644 index 000000000..0a267283d --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase1.svg @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + bbs + + + + + +l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase11SubstationGraphHorizontal.svg b/single-line-diagram-core/src/test/resources/TestCase11SubstationGraphHorizontal.svg new file mode 100644 index 000000000..007404c9b --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase11SubstationGraphHorizontal.svg @@ -0,0 +1,1953 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + + +load1 + + + + + +gen1 + + + + + +load2 + + + + + +gen2 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + +trf5 + + + line1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_ONE_TWO + + + trf6_ONE_THREE + + + + + + + + trf7_ONE_TWO + + + trf7_ONE_THREE + + + + + + + + trf8_ONE_TWO + + + trf8_ONE_THREE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs5 + + + + bbs6 + + + + + +load3 + + + + + +gen4 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_TWO_ONE + + + trf6_TWO_THREE + + + + + + + + trf7_TWO_ONE + + + trf7_TWO_THREE + + + + + + + + trf8_TWO_ONE + + + trf8_TWO_THREE + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + bbs7 + + + + + +load4 + + + + +trf5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_THREE_ONE + + + trf6_THREE_TWO + + + + + + + + trf7_THREE_ONE + + + trf7_THREE_TWO + + + + + + + + trf8_THREE_ONE + + + trf8_THREE_TWO + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + vl1 + + + vl2 + + + vl3 + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase11SubstationGraphHorizontalNominalVoltageLevel.svg b/single-line-diagram-core/src/test/resources/TestCase11SubstationGraphHorizontalNominalVoltageLevel.svg new file mode 100644 index 000000000..cbade0327 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase11SubstationGraphHorizontalNominalVoltageLevel.svg @@ -0,0 +1,1953 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + + +load1 + + + + + +gen1 + + + + + +load2 + + + + + +gen2 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + +trf5 + + + line1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_ONE_TWO + + + trf6_ONE_THREE + + + + + + + + trf7_ONE_TWO + + + trf7_ONE_THREE + + + + + + + + trf8_ONE_TWO + + + trf8_ONE_THREE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs5 + + + + bbs6 + + + + + +load3 + + + + + +gen4 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_TWO_ONE + + + trf6_TWO_THREE + + + + + + + + trf7_TWO_ONE + + + trf7_TWO_THREE + + + + + + + + trf8_TWO_ONE + + + trf8_TWO_THREE + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + bbs7 + + + + + +load4 + + + + +trf5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_THREE_ONE + + + trf6_THREE_TWO + + + + + + + + trf7_THREE_ONE + + + trf7_THREE_TWO + + + + + + + + trf8_THREE_ONE + + + trf8_THREE_TWO + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + vl1 + + + vl2 + + + vl3 + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase11SubstationGraphVertical.svg b/single-line-diagram-core/src/test/resources/TestCase11SubstationGraphVertical.svg new file mode 100644 index 000000000..bce0df0f9 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase11SubstationGraphVertical.svg @@ -0,0 +1,1953 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + + +load1 + + + + + +gen1 + + + + + +load2 + + + + + +gen2 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + +trf5 + + + line1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_ONE_TWO + + + trf6_ONE_THREE + + + + + + + + trf7_ONE_TWO + + + trf7_ONE_THREE + + + + + + + + trf8_ONE_TWO + + + trf8_ONE_THREE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs5 + + + + bbs6 + + + + + +load3 + + + + + +gen4 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_TWO_ONE + + + trf6_TWO_THREE + + + + + + + + trf7_TWO_ONE + + + trf7_TWO_THREE + + + + + + + + trf8_TWO_ONE + + + trf8_TWO_THREE + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + bbs7 + + + + + +load4 + + + + +trf5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_THREE_ONE + + + trf6_THREE_TWO + + + + + + + + trf7_THREE_ONE + + + trf7_THREE_TWO + + + + + + + + trf8_THREE_ONE + + + trf8_THREE_TWO + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + vl1 + + + vl2 + + + vl3 + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase12GraphVL1.svg b/single-line-diagram-core/src/test/resources/TestCase12GraphVL1.svg new file mode 100644 index 000000000..023842e06 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase12GraphVL1.svg @@ -0,0 +1,878 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + + +load1 + + + + + +gen1 + + + + + +load2 + + + + + +gen2 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + +trf5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_ONE_TWO + + + + + +self4_trf6 + + + + + + + + trf7_ONE_TWO + + + + + +self4_trf7 + + + + + + + + trf8_ONE_TWO + + + + + +self4_trf8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vl1 + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase12GraphVL1_optimized.svg b/single-line-diagram-core/src/test/resources/TestCase12GraphVL1_optimized.svg new file mode 100644 index 000000000..f634591d0 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase12GraphVL1_optimized.svg @@ -0,0 +1,580 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + load1 + + + + gen1 + + + + load2 + + + + gen2 + + + + + trf1 + + + + + trf2 + + + + + trf3 + + + + + trf4 + + + + + trf5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_ONE_TWO + + + + self4_trf6 + + + + + + + + trf7_ONE_TWO + + + + self4_trf7 + + + + + + + + trf8_ONE_TWO + + + + self4_trf8 + + + + + + + + + + + + + + + + + + + + + + + + + + 10 + + + + 10 + + + + + + 0 + + + + 0 + + + + + + 10 + + + + 10 + + + + + + 0 + + + + 0 + + + + + + 0 + + + + 0 + + + + + + 0 + + + + 0 + + + + + + + 0 + + + + + 0 + + + + + + + 0 + + + + + 0 + + + + + + 0 + + + + 0 + + + + + + + + + 0 + + + + 0 + + + + + + + + + + + + + 0 + + + + 0 + + + + + + + + + + + + + 0 + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vl1 + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase12GraphVL2.svg b/single-line-diagram-core/src/test/resources/TestCase12GraphVL2.svg new file mode 100644 index 000000000..9bea023e8 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase12GraphVL2.svg @@ -0,0 +1,689 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs5 + + + + bbs6 + + + + + +load3 + + + + + +gen4 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_TWO_ONE + + + + + +self4_trf6 + + + + + + + + trf7_TWO_ONE + + + + + +self4_trf7 + + + + + + + + trf8_TWO_ONE + + + + + +self4_trf8 + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vl2 + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase12GraphVL2_optimized.svg b/single-line-diagram-core/src/test/resources/TestCase12GraphVL2_optimized.svg new file mode 100644 index 000000000..798ee53a8 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase12GraphVL2_optimized.svg @@ -0,0 +1,483 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs5 + + + + bbs6 + + + + load3 + + + + gen4 + + + + + trf1 + + + + + trf2 + + + + + trf3 + + + + + trf4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_TWO_ONE + + + + self4_trf6 + + + + + + + + trf7_TWO_ONE + + + + self4_trf7 + + + + + + + + trf8_TWO_ONE + + + + self4_trf8 + + + + + + + + + + + + + + + + + + + 10 + + + + 10 + + + + + + 0 + + + + 0 + + + + + + 0 + + + + 0 + + + + + + + 0 + + + + + 0 + + + + + + + 0 + + + + + 0 + + + + + + 0 + + + + 0 + + + + + + + + + 0 + + + + 0 + + + + + + + + + + + + + 0 + + + + 0 + + + + + + + + + + + + + 0 + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vl2 + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase12GraphVL3.svg b/single-line-diagram-core/src/test/resources/TestCase12GraphVL3.svg new file mode 100644 index 000000000..a1bcb01e6 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase12GraphVL3.svg @@ -0,0 +1,603 @@ + + + + + + + + + + + + + + + + + + + + + + + + bbs7 + + + + + +load4 + + + + + +self4 + + + + +trf5 + + + + + +self5 + + + + bbs8 + + + + + + +self6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_THREE_ONE + + + trf6_THREE_TWO + + + + + + + + trf7_THREE_ONE + + + trf7_THREE_TWO + + + + + + + + trf8_THREE_ONE + + + trf8_THREE_TWO + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + vl3 + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase12GraphVL3_optimized.svg b/single-line-diagram-core/src/test/resources/TestCase12GraphVL3_optimized.svg new file mode 100644 index 000000000..731d226fb --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase12GraphVL3_optimized.svg @@ -0,0 +1,439 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs7 + + + + load4 + + + + self4 + + + + + trf5 + + + + self5 + + + + bbs8 + + + + self6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_THREE_ONE + + + trf6_THREE_TWO + + + + + + + + trf7_THREE_ONE + + + trf7_THREE_TWO + + + + + + + + trf8_THREE_ONE + + + trf8_THREE_TWO + + + + + + + + + + + + + + 10 + + + + 10 + + + + + + 0 + + + + 0 + + + + + + + 0 + + + + + 0 + + + + + + + + + 0 + + + + 0 + + + + + + 0 + + + + 0 + + + + + + 0 + + + + 0 + + + + + 0 + + + + 0 + + + + + + 0 + + + + 0 + + + + + 0 + + + + 0 + + + + + + 0 + + + + 0 + + + + + 0 + + + + 0 + + + + + + + + + + + + + + + + + + + vl3 + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase1BusBreaker.svg b/single-line-diagram-core/src/test/resources/TestCase1BusBreaker.svg new file mode 100644 index 000000000..ce5c0758e --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase1BusBreaker.svg @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + b1 + + + + b2 + + + + + +l + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase1inverted.svg b/single-line-diagram-core/src/test/resources/TestCase1inverted.svg new file mode 100644 index 000000000..446a2b068 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase1inverted.svg @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + +l + + + + bbs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase2StackedCell.svg b/single-line-diagram-core/src/test/resources/TestCase2StackedCell.svg new file mode 100644 index 000000000..b5bbd7e5e --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase2StackedCell.svg @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + + +l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase3Coupling.svg b/single-line-diagram-core/src/test/resources/TestCase3Coupling.svg new file mode 100644 index 000000000..84491167d --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase3Coupling.svg @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase4NotParallelel.svg b/single-line-diagram-core/src/test/resources/TestCase4NotParallelel.svg new file mode 100644 index 000000000..2a8c1934e --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase4NotParallelel.svg @@ -0,0 +1,340 @@ + + + + + + + + + + + + + + + + + bbs1.1 + + + + bbs1.2 + + + + bbs2.1 + + + + + +la + + + + + +lb + + + + + +gc + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase5ShuntHorizontal.svg b/single-line-diagram-core/src/test/resources/TestCase5ShuntHorizontal.svg new file mode 100644 index 000000000..fde86333d --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase5ShuntHorizontal.svg @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + bbs + + + + + +la + + + + + +lb + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase5ShuntVertical.svg b/single-line-diagram-core/src/test/resources/TestCase5ShuntVertical.svg new file mode 100644 index 000000000..e61d2f9dc --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase5ShuntVertical.svg @@ -0,0 +1,247 @@ + + + + + + + + + + + + + + + bbs + + + + + +la + + + + + +lb + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestCase6CouplingNonFlatHorizontal.svg b/single-line-diagram-core/src/test/resources/TestCase6CouplingNonFlatHorizontal.svg new file mode 100644 index 000000000..2b5df66f2 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestCase6CouplingNonFlatHorizontal.svg @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + bbs1.1 + + + + bbs1.2 + + + + bbs2.1 + + + + bbs2.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestDefaultFeedersPosition.svg b/single-line-diagram-core/src/test/resources/TestDefaultFeedersPosition.svg new file mode 100644 index 000000000..ecb1aa707 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestDefaultFeedersPosition.svg @@ -0,0 +1,521 @@ + + + + + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + + +load1 + + + + + +gen1 + + + + + +load2 + + + + + +gen2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + vl1 + + + diff --git a/single-line-diagram-core/src/test/resources/TestDefaultFeedersPosition2.svg b/single-line-diagram-core/src/test/resources/TestDefaultFeedersPosition2.svg new file mode 100644 index 000000000..6fc4d4341 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestDefaultFeedersPosition2.svg @@ -0,0 +1,509 @@ + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + + +load1 + + + + + +gen1 + + + + + +load2 + + + + + +gen2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + vl1 + + + diff --git a/single-line-diagram-core/src/test/resources/TestShiftFeedersPosition.svg b/single-line-diagram-core/src/test/resources/TestShiftFeedersPosition.svg new file mode 100644 index 000000000..5a6f611b5 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestShiftFeedersPosition.svg @@ -0,0 +1,521 @@ + + + + + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + + +load1 + + + + + +gen1 + + + + + +load2 + + + + + +gen2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + vl1 + + + diff --git a/single-line-diagram-core/src/test/resources/TestSubstation.svg b/single-line-diagram-core/src/test/resources/TestSubstation.svg new file mode 100644 index 000000000..6b7f07176 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestSubstation.svg @@ -0,0 +1,281 @@ + + + + + + + bbs + + + + + +l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestUnicityNodeIdNetWork1.svg b/single-line-diagram-core/src/test/resources/TestUnicityNodeIdNetWork1.svg new file mode 100644 index 000000000..7946cfa31 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestUnicityNodeIdNetWork1.svg @@ -0,0 +1,170 @@ + + + + + + bbs + + + + + +l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestUnicityNodeIdNetWork2.svg b/single-line-diagram-core/src/test/resources/TestUnicityNodeIdNetWork2.svg new file mode 100644 index 000000000..274857778 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestUnicityNodeIdNetWork2.svg @@ -0,0 +1,170 @@ + + + + + + bbs + + + + + +l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/TestVL.svg b/single-line-diagram-core/src/test/resources/TestVL.svg new file mode 100644 index 000000000..661daa3e9 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/TestVL.svg @@ -0,0 +1,170 @@ + + + + + + bbs + + + + + +l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/base-voltages.yml b/single-line-diagram-core/src/test/resources/base-voltages.yml new file mode 100644 index 000000000..e2736f18e --- /dev/null +++ b/single-line-diagram-core/src/test/resources/base-voltages.yml @@ -0,0 +1,18 @@ +baseVoltages: + - name: "0" + minValue: 0 + maxValue: 1 + color: "#808080" + profile: "RTE" + - name: "400" + minValue: 300 + maxValue: 500 + color: "#FF3333" + profile: "RTE" + - name: "225" + minValue: 180 + maxValue: 300 + color: "#33FF33" + profile: "RTE" + +defaultProfile: "RTE" \ No newline at end of file diff --git a/single-line-diagram-core/src/test/resources/nominalVoltage.svg b/single-line-diagram-core/src/test/resources/nominalVoltage.svg new file mode 100644 index 000000000..b12a1eaec --- /dev/null +++ b/single-line-diagram-core/src/test/resources/nominalVoltage.svg @@ -0,0 +1,281 @@ + + + + + + + bbs + + + + + +l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/substDiag.svg b/single-line-diagram-core/src/test/resources/substDiag.svg new file mode 100644 index 000000000..007404c9b --- /dev/null +++ b/single-line-diagram-core/src/test/resources/substDiag.svg @@ -0,0 +1,1953 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + + +load1 + + + + + +gen1 + + + + + +load2 + + + + + +gen2 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + +trf5 + + + line1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_ONE_TWO + + + trf6_ONE_THREE + + + + + + + + trf7_ONE_TWO + + + trf7_ONE_THREE + + + + + + + + trf8_ONE_TWO + + + trf8_ONE_THREE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs5 + + + + bbs6 + + + + + +load3 + + + + + +gen4 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_TWO_ONE + + + trf6_TWO_THREE + + + + + + + + trf7_TWO_ONE + + + trf7_TWO_THREE + + + + + + + + trf8_TWO_ONE + + + trf8_TWO_THREE + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + bbs7 + + + + + +load4 + + + + +trf5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_THREE_ONE + + + trf6_THREE_TWO + + + + + + + + trf7_THREE_ONE + + + trf7_THREE_TWO + + + + + + + + trf8_THREE_ONE + + + trf8_THREE_TWO + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + vl1 + + + vl2 + + + vl3 + + + diff --git a/single-line-diagram-core/src/test/resources/substDiag_metadata.json b/single-line-diagram-core/src/test/resources/substDiag_metadata.json new file mode 100644 index 000000000..9758bdc3d --- /dev/null +++ b/single-line-diagram-core/src/test/resources/substDiag_metadata.json @@ -0,0 +1,2878 @@ +{ + "components" : [ { + "type" : "BUSBAR_SECTION", + "id" : "bbs7", + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 15.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 40.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 65.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 90.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 115.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 140.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 165.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 190.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 215.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 240.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 265.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 290.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 315.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 340.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 365.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 380.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + } ], + "size" : { + "width" : 0.0, + "height" : 0.0 + } + }, { + "type" : "GENERATOR", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : -6.0, + "orientation" : "VERTICAL" + }, { + "x" : 0.0, + "y" : 6.0, + "orientation" : "VERTICAL" + }, { + "x" : -6.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 6.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + } ], + "size" : { + "width" : 12.0, + "height" : 12.0 + } + }, { + "type" : "ARROW", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 10.0, + "height" : 10.0 + } + }, { + "type" : "LOAD", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : -4.0, + "orientation" : "VERTICAL" + }, { + "x" : 0.0, + "y" : 4.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 16.0, + "height" : 9.0 + } + }, { + "type" : "NODE", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "NONE" + } ], + "size" : { + "width" : 8.0, + "height" : 8.0 + } + }, { + "type" : "LINE", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "NONE" + } ], + "size" : { + "width" : 0.0, + "height" : 0.0 + } + }, { + "type" : "DISCONNECTOR", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 8.0, + "height" : 8.0 + } + }, { + "type" : "TWO_WINDINGS_TRANSFORMER", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : -4.0, + "orientation" : "VERTICAL" + }, { + "x" : 0.0, + "y" : 4.0, + "orientation" : "VERTICAL" + }, { + "x" : -6.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 6.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + } ], + "size" : { + "width" : 13.0, + "height" : 8.0 + } + }, { + "type" : "THREE_WINDINGS_TRANSFORMER", + "id" : null, + "anchorPoints" : [ { + "x" : -7.0, + "y" : -4.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 7.0, + "y" : -4.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 0.0, + "y" : 6.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 14.0, + "height" : 12.0 + } + }, { + "type" : "BREAKER", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : -10.0, + "orientation" : "VERTICAL" + }, { + "x" : 0.0, + "y" : 10.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 20.0, + "height" : 20.0 + } + } ], + "nodes" : [ { + "id" : "dtrct21", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf3_ONE", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf15Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf1_TWO", + "vid" : "vl2", + "nextVId" : "vl1", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "trf6_ONE_THREE", + "vid" : "vl1", + "nextVId" : "vl3", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "trf7_TWO_THREE", + "vid" : "vl2", + "nextVId" : "vl3", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf12Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf22", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf37", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf23", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf38", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf21", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf7_THREE_ONE", + "vid" : "vl3", + "nextVId" : "vl1", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "btrf26", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf27", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf24", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf25", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf36", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf6_TWO_ONE", + "vid" : "vl2", + "nextVId" : "vl1", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "btrf28", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf5_ONE", + "vid" : "vl1", + "nextVId" : "vl3", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "trf6_THREE_TWO", + "vid" : "vl3", + "nextVId" : "vl2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "load3", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "LOAD", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl2_dscpl1Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "load2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "LOAD", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "load4", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "LOAD", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl3_trf83_fictif", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl3_dtrf36Fictif", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "load1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "LOAD", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "trf8_THREE_ONE", + "vid" : "vl3", + "nextVId" : "vl1", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl1_trf61_fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dgen2Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf26", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf27", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf28", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf22", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf37", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf23", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf6_TWO_THREE", + "vid" : "vl2", + "nextVId" : "vl3", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "btrf38", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dsect22Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf24", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf25", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf36", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf7_ONE_THREE", + "vid" : "vl1", + "nextVId" : "vl3", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "FICT_vl2_dtrf28Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf21", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_dgen4Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf8_ONE_TWO", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "bbs7", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs5", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs6", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs3", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs4", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_dscpl2Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl3_dload4Fictif", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf17Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf4_TWO", + "vid" : "vl2", + "nextVId" : "vl1", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "trf7_TWO_ONE", + "vid" : "vl2", + "nextVId" : "vl1", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "dtrct11", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "LABEL_VL_vl1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : null, + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : true + }, { + "id" : "LABEL_VL_vl3", + "vid" : "vl3", + "nextVId" : null, + "componentType" : null, + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : true + }, { + "id" : "GRID_vl3", + "vid" : "vl3", + "nextVId" : null, + "componentType" : null, + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_trf82_fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : 180.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl3_trf73_fictif", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : 180.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "LABEL_VL_vl2", + "vid" : "vl2", + "nextVId" : null, + "componentType" : null, + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : true + }, { + "id" : "GRID_vl2", + "vid" : "vl2", + "nextVId" : null, + "componentType" : null, + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "GRID_vl1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : null, + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_dtrf26Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf11Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf8_TWO_THREE", + "vid" : "vl2", + "nextVId" : "vl3", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "btrf11", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf12", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf15", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dsect12Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf16", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf7_THREE_TWO", + "vid" : "vl3", + "nextVId" : "vl2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "btrf13", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf14", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dline11_2Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf18Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf2_TWO", + "vid" : "vl2", + "nextVId" : "vl1", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "btrf17", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf18", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dsect11Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dsect21Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl3_dtrf25Fictif", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_dtrf27Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bload1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bload2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bload3", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bload4", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_dtrf24Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf2_ONE", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl1_dload2Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf16Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "line1_ONE", + "vid" : "vl1", + "nextVId" : "vlSubst2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "dscpl2", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_trf81_fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dscpl1", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "ddcpl1", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dline11_2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_trf72_fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_dtrf21Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf8_TWO_ONE", + "vid" : "vl2", + "nextVId" : "vl1", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf13Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_dload3Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf4_ONE", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "trf6_ONE_TWO", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl3_trf63_fictif", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl3_dtrf37Fictif", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf6_THREE_ONE", + "vid" : "vl3", + "nextVId" : "vl1", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "dsect21", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dsect22", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf5_TWO", + "vid" : "vl3", + "nextVId" : "vl1", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "gen1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "GENERATOR", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "bgen2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "gen2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "GENERATOR", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "bline11_2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dgen1Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bgen4", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_dtrf22Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf15", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "gen4", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "GENERATOR", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "dtrf16", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bgen1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf17", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf18", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf11", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf8_ONE_THREE", + "vid" : "vl1", + "nextVId" : "vl3", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "dtrf12", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf13", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf14Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf14", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf8_THREE_TWO", + "vid" : "vl3", + "nextVId" : "vl2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "trf3_TWO", + "vid" : "vl2", + "nextVId" : "vl1", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "dload4", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dsect12", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dload2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dload3", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dload1Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_trf71_fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : 180.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf1_ONE", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "dsect11", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dload1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_dtrf23Fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl3_dtrf38Fictif", + "vid" : "vl3", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dgen1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl2_trf62_fictif", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dgen2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dgen4", + "vid" : "vl2", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf7_ONE_TWO", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + } ], + "wires" : [ { + "id" : "vl1_Wire62", + "nodeId1" : "btrf17", + "nodeId2" : "FICT_vl1_dtrf17Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire61", + "nodeId1" : "dtrf13", + "nodeId2" : "FICT_vl1_dtrf13Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire64", + "nodeId1" : "dtrct21", + "nodeId2" : "FICT_vl1_dsect22Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire63", + "nodeId1" : "dtrf17", + "nodeId2" : "FICT_vl1_dtrf17Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire60", + "nodeId1" : "btrf13", + "nodeId2" : "FICT_vl1_dtrf13Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire69", + "nodeId1" : "dtrf14", + "nodeId2" : "FICT_vl1_dtrf14Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire66", + "nodeId1" : "bgen2", + "nodeId2" : "FICT_vl1_dgen2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire65", + "nodeId1" : "dsect22", + "nodeId2" : "FICT_vl1_dsect22Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire68", + "nodeId1" : "btrf14", + "nodeId2" : "FICT_vl1_dtrf14Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire67", + "nodeId1" : "dgen2", + "nodeId2" : "FICT_vl1_dgen2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_vl3_Wire10", + "nodeId1" : "trf7_TWO_THREE", + "nodeId2" : "trf7_THREE_TWO", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl2_vl3_Wire13", + "nodeId1" : "trf8_TWO_THREE", + "nodeId2" : "trf8_THREE_TWO", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_Wire51", + "nodeId1" : "dload2", + "nodeId2" : "FICT_vl1_dload2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire50", + "nodeId1" : "bload2", + "nodeId2" : "FICT_vl1_dload2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire53", + "nodeId1" : "dtrf12", + "nodeId2" : "FICT_vl1_dtrf12Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire52", + "nodeId1" : "btrf12", + "nodeId2" : "FICT_vl1_dtrf12Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire59", + "nodeId1" : "dgen1", + "nodeId2" : "FICT_vl1_dgen1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire58", + "nodeId1" : "bgen1", + "nodeId2" : "FICT_vl1_dgen1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire55", + "nodeId1" : "dtrf18", + "nodeId2" : "FICT_vl1_dtrf18Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire54", + "nodeId1" : "btrf18", + "nodeId2" : "FICT_vl1_dtrf18Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire57", + "nodeId1" : "dsect21", + "nodeId2" : "FICT_vl1_dsect21Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire56", + "nodeId1" : "dtrct21", + "nodeId2" : "FICT_vl1_dsect21Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire19", + "nodeId1" : "FICT_vl2_trf62_fictif", + "nodeId2" : "trf6_TWO_THREE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire10", + "nodeId1" : "bbs6", + "nodeId2" : "dtrf23", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_vl3_Wire9", + "nodeId1" : "trf7_ONE_THREE", + "nodeId2" : "trf7_THREE_ONE", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_vl3_Wire4", + "nodeId1" : "trf5_ONE", + "nodeId2" : "trf5_TWO", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_vl3_Wire6", + "nodeId1" : "trf6_ONE_THREE", + "nodeId2" : "trf6_THREE_ONE", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl2_Wire17", + "nodeId1" : "btrf26", + "nodeId2" : "FICT_vl2_trf62_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire18", + "nodeId1" : "FICT_vl2_trf62_fictif", + "nodeId2" : "trf6_TWO_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire15", + "nodeId1" : "bbs5", + "nodeId2" : "dtrf27", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire16", + "nodeId1" : "bbs6", + "nodeId2" : "dtrf28", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire13", + "nodeId1" : "btrf24", + "nodeId2" : "trf4_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire14", + "nodeId1" : "bbs6", + "nodeId2" : "dtrf26", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire11", + "nodeId1" : "btrf23", + "nodeId2" : "trf3_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire12", + "nodeId1" : "bbs5", + "nodeId2" : "dtrf24", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire9", + "nodeId1" : "load2", + "nodeId2" : "bload2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire29", + "nodeId1" : "FICT_vl1_trf61_fictif", + "nodeId2" : "trf6_ONE_THREE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire20", + "nodeId1" : "btrf27", + "nodeId2" : "FICT_vl2_trf72_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire22", + "nodeId1" : "btrf37", + "nodeId2" : "FICT_vl3_dtrf37Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_vl2_Wire3", + "nodeId1" : "trf4_ONE", + "nodeId2" : "trf4_TWO", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl2_Wire21", + "nodeId1" : "FICT_vl2_trf72_fictif", + "nodeId2" : "trf7_TWO_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire21", + "nodeId1" : "dtrf36", + "nodeId2" : "FICT_vl3_dtrf36Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_vl2_Wire2", + "nodeId1" : "trf3_ONE", + "nodeId2" : "trf3_TWO", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_Wire20", + "nodeId1" : "bbs1", + "nodeId2" : "dtrf15", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire20", + "nodeId1" : "btrf36", + "nodeId2" : "FICT_vl3_dtrf36Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_vl2_Wire1", + "nodeId1" : "trf2_ONE", + "nodeId2" : "trf2_TWO", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_vl2_Wire0", + "nodeId1" : "trf1_ONE", + "nodeId2" : "trf1_TWO", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_Wire0", + "nodeId1" : "bbs1", + "nodeId2" : "dsect11", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_vl3_Wire12", + "nodeId1" : "trf8_ONE_THREE", + "nodeId2" : "trf8_THREE_ONE", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_Wire4", + "nodeId1" : "bbs1", + "nodeId2" : "dload1", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire26", + "nodeId1" : "bline11_2", + "nodeId2" : "line1_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire28", + "nodeId1" : "bload3", + "nodeId2" : "FICT_vl2_dload3Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire3", + "nodeId1" : "dsect22", + "nodeId2" : "bbs4", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire25", + "nodeId1" : "bbs1", + "nodeId2" : "dline11_2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire29", + "nodeId1" : "dload3", + "nodeId2" : "FICT_vl2_dload3Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire2", + "nodeId1" : "bbs3", + "nodeId2" : "dsect21", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire28", + "nodeId1" : "FICT_vl1_trf61_fictif", + "nodeId2" : "trf6_ONE_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire26", + "nodeId1" : "ddcpl1", + "nodeId2" : "FICT_vl2_dscpl1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_vl2_Wire11", + "nodeId1" : "trf8_ONE_TWO", + "nodeId2" : "trf8_TWO_ONE", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_Wire1", + "nodeId1" : "dsect12", + "nodeId2" : "bbs2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire27", + "nodeId1" : "btrf16", + "nodeId2" : "FICT_vl1_trf61_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire27", + "nodeId1" : "dscpl1", + "nodeId2" : "FICT_vl2_dscpl1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_vl2_Wire8", + "nodeId1" : "trf7_ONE_TWO", + "nodeId2" : "trf7_TWO_ONE", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_Wire8", + "nodeId1" : "bbs2", + "nodeId2" : "dload2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire22", + "nodeId1" : "bbs1", + "nodeId2" : "dtrf16", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire24", + "nodeId1" : "FICT_vl2_trf82_fictif", + "nodeId2" : "trf8_TWO_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire7", + "nodeId1" : "gen1", + "nodeId2" : "bgen1", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire21", + "nodeId1" : "btrf15", + "nodeId2" : "trf5_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire25", + "nodeId1" : "FICT_vl2_trf82_fictif", + "nodeId2" : "trf8_TWO_THREE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire25", + "nodeId1" : "dtrf38", + "nodeId2" : "FICT_vl3_dtrf38Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire6", + "nodeId1" : "bbs3", + "nodeId2" : "dgen1", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire24", + "nodeId1" : "bbs2", + "nodeId2" : "dtrf18", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire22", + "nodeId1" : "FICT_vl2_trf72_fictif", + "nodeId2" : "trf7_TWO_THREE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire24", + "nodeId1" : "btrf38", + "nodeId2" : "FICT_vl3_dtrf38Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_vl2_Wire5", + "nodeId1" : "trf6_ONE_TWO", + "nodeId2" : "trf6_TWO_ONE", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl1_Wire5", + "nodeId1" : "load1", + "nodeId2" : "bload1", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire23", + "nodeId1" : "bbs3", + "nodeId2" : "dtrf17", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire23", + "nodeId1" : "btrf28", + "nodeId2" : "FICT_vl2_trf82_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire23", + "nodeId1" : "dtrf37", + "nodeId2" : "FICT_vl3_dtrf37Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire19", + "nodeId1" : "btrf14", + "nodeId2" : "trf4_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire18", + "nodeId1" : "bbs4", + "nodeId2" : "dtrf14", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_vl3_Wire7", + "nodeId1" : "trf6_TWO_THREE", + "nodeId2" : "trf6_THREE_TWO", + "straight" : false, + "snakeLine" : true + }, { + "id" : "vl2_Wire31", + "nodeId1" : "dtrf21", + "nodeId2" : "FICT_vl2_dtrf21Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire11", + "nodeId1" : "FICT_vl3_trf73_fictif", + "nodeId2" : "trf7_THREE_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire32", + "nodeId1" : "btrf24", + "nodeId2" : "FICT_vl2_dtrf24Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire10", + "nodeId1" : "btrf37", + "nodeId2" : "FICT_vl3_trf73_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire30", + "nodeId1" : "btrf21", + "nodeId2" : "FICT_vl2_dtrf21Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire15", + "nodeId1" : "btrf12", + "nodeId2" : "trf2_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire39", + "nodeId1" : "dgen4", + "nodeId2" : "FICT_vl2_dgen4Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire19", + "nodeId1" : "dtrf25", + "nodeId2" : "FICT_vl3_dtrf25Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire14", + "nodeId1" : "bbs2", + "nodeId2" : "dtrf12", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire18", + "nodeId1" : "btrf25", + "nodeId2" : "FICT_vl3_dtrf25Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire17", + "nodeId1" : "btrf13", + "nodeId2" : "trf3_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire37", + "nodeId1" : "dscpl2", + "nodeId2" : "FICT_vl2_dscpl2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire17", + "nodeId1" : "dload4", + "nodeId2" : "FICT_vl3_dload4Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire16", + "nodeId1" : "bbs3", + "nodeId2" : "dtrf13", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire38", + "nodeId1" : "bgen4", + "nodeId2" : "FICT_vl2_dgen4Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire16", + "nodeId1" : "bload4", + "nodeId2" : "FICT_vl3_dload4Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire11", + "nodeId1" : "gen2", + "nodeId2" : "bgen2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire35", + "nodeId1" : "dtrf27", + "nodeId2" : "FICT_vl2_dtrf27Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire15", + "nodeId1" : "FICT_vl3_trf83_fictif", + "nodeId2" : "trf8_THREE_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire10", + "nodeId1" : "bbs4", + "nodeId2" : "dgen2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire36", + "nodeId1" : "ddcpl1", + "nodeId2" : "FICT_vl2_dscpl2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire14", + "nodeId1" : "FICT_vl3_trf83_fictif", + "nodeId2" : "trf8_THREE_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire13", + "nodeId1" : "btrf11", + "nodeId2" : "trf1_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire33", + "nodeId1" : "dtrf24", + "nodeId2" : "FICT_vl2_dtrf24Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire13", + "nodeId1" : "btrf38", + "nodeId2" : "FICT_vl3_trf83_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire12", + "nodeId1" : "bbs1", + "nodeId2" : "dtrf11", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire34", + "nodeId1" : "btrf27", + "nodeId2" : "FICT_vl2_dtrf27Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire12", + "nodeId1" : "FICT_vl3_trf73_fictif", + "nodeId2" : "trf7_THREE_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire9", + "nodeId1" : "FICT_vl3_trf63_fictif", + "nodeId2" : "trf6_THREE_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire8", + "nodeId1" : "FICT_vl3_trf63_fictif", + "nodeId2" : "trf6_THREE_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire7", + "nodeId1" : "btrf36", + "nodeId2" : "FICT_vl3_trf63_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire40", + "nodeId1" : "btrf11", + "nodeId2" : "FICT_vl1_dtrf11Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire42", + "nodeId1" : "btrf23", + "nodeId2" : "FICT_vl2_dtrf23Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire6", + "nodeId1" : "bbs7", + "nodeId2" : "dtrf38", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire43", + "nodeId1" : "dtrf23", + "nodeId2" : "FICT_vl2_dtrf23Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire5", + "nodeId1" : "bbs7", + "nodeId2" : "dtrf37", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire42", + "nodeId1" : "btrf15", + "nodeId2" : "FICT_vl1_dtrf15Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire40", + "nodeId1" : "btrf22", + "nodeId2" : "FICT_vl2_dtrf22Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire4", + "nodeId1" : "bbs7", + "nodeId2" : "dtrf36", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire41", + "nodeId1" : "dtrf11", + "nodeId2" : "FICT_vl1_dtrf11Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire41", + "nodeId1" : "dtrf22", + "nodeId2" : "FICT_vl2_dtrf22Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire3", + "nodeId1" : "btrf25", + "nodeId2" : "trf5_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire2", + "nodeId1" : "bbs7", + "nodeId2" : "dtrf25", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire1", + "nodeId1" : "bload4", + "nodeId2" : "load4", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl3_Wire0", + "nodeId1" : "bbs7", + "nodeId2" : "dload4", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire48", + "nodeId1" : "dtrct11", + "nodeId2" : "FICT_vl1_dsect12Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire47", + "nodeId1" : "dline11_2", + "nodeId2" : "FICT_vl1_dline11_2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire49", + "nodeId1" : "dsect12", + "nodeId2" : "FICT_vl1_dsect12Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire44", + "nodeId1" : "btrf16", + "nodeId2" : "FICT_vl1_dtrf16Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire46", + "nodeId1" : "btrf28", + "nodeId2" : "FICT_vl2_dtrf28Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire43", + "nodeId1" : "dtrf15", + "nodeId2" : "FICT_vl1_dtrf15Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire47", + "nodeId1" : "dtrf28", + "nodeId2" : "FICT_vl2_dtrf28Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire46", + "nodeId1" : "bline11_2", + "nodeId2" : "FICT_vl1_dline11_2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire44", + "nodeId1" : "btrf26", + "nodeId2" : "FICT_vl2_dtrf26Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire45", + "nodeId1" : "dtrf16", + "nodeId2" : "FICT_vl1_dtrf16Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire45", + "nodeId1" : "dtrf26", + "nodeId2" : "FICT_vl2_dtrf26Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire9", + "nodeId1" : "btrf22", + "nodeId2" : "trf2_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire8", + "nodeId1" : "bbs6", + "nodeId2" : "dtrf22", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire1", + "nodeId1" : "dscpl2", + "nodeId2" : "bbs6", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire0", + "nodeId1" : "bbs5", + "nodeId2" : "dscpl1", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire31", + "nodeId1" : "FICT_vl1_trf71_fictif", + "nodeId2" : "trf7_ONE_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire3", + "nodeId1" : "load3", + "nodeId2" : "bload3", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire30", + "nodeId1" : "btrf17", + "nodeId2" : "FICT_vl1_trf71_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire2", + "nodeId1" : "bbs5", + "nodeId2" : "dload3", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire5", + "nodeId1" : "gen4", + "nodeId2" : "bgen4", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire4", + "nodeId1" : "bbs6", + "nodeId2" : "dgen4", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire7", + "nodeId1" : "btrf21", + "nodeId2" : "trf1_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl2_Wire6", + "nodeId1" : "bbs5", + "nodeId2" : "dtrf21", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire37", + "nodeId1" : "dsect11", + "nodeId2" : "FICT_vl1_dsect11Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire36", + "nodeId1" : "dtrct11", + "nodeId2" : "FICT_vl1_dsect11Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire39", + "nodeId1" : "dload1", + "nodeId2" : "FICT_vl1_dload1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire38", + "nodeId1" : "bload1", + "nodeId2" : "FICT_vl1_dload1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire33", + "nodeId1" : "btrf18", + "nodeId2" : "FICT_vl1_trf81_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire32", + "nodeId1" : "FICT_vl1_trf71_fictif", + "nodeId2" : "trf7_ONE_THREE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire35", + "nodeId1" : "FICT_vl1_trf81_fictif", + "nodeId2" : "trf8_ONE_THREE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire34", + "nodeId1" : "FICT_vl1_trf81_fictif", + "nodeId2" : "trf8_ONE_TWO", + "straight" : false, + "snakeLine" : false + } ], + "arrows" : [ { + "id" : "vl1_Wire26_ARROW1", + "wireId" : "vl1_Wire26", + "distance" : 20.0 + }, { + "id" : "vl1_Wire26_ARROW2", + "wireId" : "vl1_Wire26", + "distance" : 20.0 + }, { + "id" : "vl3_Wire3_ARROW2", + "wireId" : "vl3_Wire3", + "distance" : 20.0 + }, { + "id" : "vl1_Wire28_ARROW1", + "wireId" : "vl1_Wire28", + "distance" : 20.0 + }, { + "id" : "vl3_Wire1_ARROW2", + "wireId" : "vl3_Wire1", + "distance" : 20.0 + }, { + "id" : "vl1_Wire28_ARROW2", + "wireId" : "vl1_Wire28", + "distance" : 20.0 + }, { + "id" : "vl1_Wire29_ARROW2", + "wireId" : "vl1_Wire29", + "distance" : 20.0 + }, { + "id" : "vl3_Wire3_ARROW1", + "wireId" : "vl3_Wire3", + "distance" : 20.0 + }, { + "id" : "vl3_Wire1_ARROW1", + "wireId" : "vl3_Wire1", + "distance" : 20.0 + }, { + "id" : "vl1_Wire29_ARROW1", + "wireId" : "vl1_Wire29", + "distance" : 20.0 + }, { + "id" : "vl1_Wire21_ARROW1", + "wireId" : "vl1_Wire21", + "distance" : 20.0 + }, { + "id" : "vl2_Wire21_ARROW1", + "wireId" : "vl2_Wire21", + "distance" : 20.0 + }, { + "id" : "vl2_Wire21_ARROW2", + "wireId" : "vl2_Wire21", + "distance" : 20.0 + }, { + "id" : "vl2_Wire22_ARROW2", + "wireId" : "vl2_Wire22", + "distance" : 20.0 + }, { + "id" : "vl3_Wire14_ARROW2", + "wireId" : "vl3_Wire14", + "distance" : 20.0 + }, { + "id" : "vl3_Wire15_ARROW2", + "wireId" : "vl3_Wire15", + "distance" : 20.0 + }, { + "id" : "vl3_Wire12_ARROW1", + "wireId" : "vl3_Wire12", + "distance" : 20.0 + }, { + "id" : "vl3_Wire14_ARROW1", + "wireId" : "vl3_Wire14", + "distance" : 20.0 + }, { + "id" : "vl3_Wire15_ARROW1", + "wireId" : "vl3_Wire15", + "distance" : 20.0 + }, { + "id" : "vl3_Wire11_ARROW2", + "wireId" : "vl3_Wire11", + "distance" : 20.0 + }, { + "id" : "vl3_Wire11_ARROW1", + "wireId" : "vl3_Wire11", + "distance" : 20.0 + }, { + "id" : "vl2_Wire25_ARROW1", + "wireId" : "vl2_Wire25", + "distance" : 20.0 + }, { + "id" : "vl2_Wire25_ARROW2", + "wireId" : "vl2_Wire25", + "distance" : 20.0 + }, { + "id" : "vl2_Wire18_ARROW2", + "wireId" : "vl2_Wire18", + "distance" : 20.0 + }, { + "id" : "vl2_Wire18_ARROW1", + "wireId" : "vl2_Wire18", + "distance" : 20.0 + }, { + "id" : "vl1_Wire9_ARROW2", + "wireId" : "vl1_Wire9", + "distance" : 20.0 + }, { + "id" : "vl2_Wire9_ARROW1", + "wireId" : "vl2_Wire9", + "distance" : 20.0 + }, { + "id" : "vl2_Wire9_ARROW2", + "wireId" : "vl2_Wire9", + "distance" : 20.0 + }, { + "id" : "vl3_Wire9_ARROW1", + "wireId" : "vl3_Wire9", + "distance" : 20.0 + }, { + "id" : "vl3_Wire9_ARROW2", + "wireId" : "vl3_Wire9", + "distance" : 20.0 + }, { + "id" : "vl1_Wire21_ARROW2", + "wireId" : "vl1_Wire21", + "distance" : 20.0 + }, { + "id" : "vl1_Wire5_ARROW2", + "wireId" : "vl1_Wire5", + "distance" : 20.0 + }, { + "id" : "vl1_Wire32_ARROW2", + "wireId" : "vl1_Wire32", + "distance" : 20.0 + }, { + "id" : "vl2_Wire5_ARROW2", + "wireId" : "vl2_Wire5", + "distance" : 20.0 + }, { + "id" : "vl2_Wire5_ARROW1", + "wireId" : "vl2_Wire5", + "distance" : 20.0 + }, { + "id" : "vl1_Wire7_ARROW2", + "wireId" : "vl1_Wire7", + "distance" : 20.0 + }, { + "id" : "vl2_Wire7_ARROW1", + "wireId" : "vl2_Wire7", + "distance" : 20.0 + }, { + "id" : "vl2_Wire7_ARROW2", + "wireId" : "vl2_Wire7", + "distance" : 20.0 + }, { + "id" : "vl1_Wire5_ARROW1", + "wireId" : "vl1_Wire5", + "distance" : 20.0 + }, { + "id" : "vl1_Wire7_ARROW1", + "wireId" : "vl1_Wire7", + "distance" : 20.0 + }, { + "id" : "vl1_Wire9_ARROW1", + "wireId" : "vl1_Wire9", + "distance" : 20.0 + }, { + "id" : "vl1_Wire35_ARROW2", + "wireId" : "vl1_Wire35", + "distance" : 20.0 + }, { + "id" : "vl1_Wire34_ARROW1", + "wireId" : "vl1_Wire34", + "distance" : 20.0 + }, { + "id" : "vl1_Wire35_ARROW1", + "wireId" : "vl1_Wire35", + "distance" : 20.0 + }, { + "id" : "vl1_Wire34_ARROW2", + "wireId" : "vl1_Wire34", + "distance" : 20.0 + }, { + "id" : "vl2_Wire3_ARROW2", + "wireId" : "vl2_Wire3", + "distance" : 20.0 + }, { + "id" : "vl2_Wire3_ARROW1", + "wireId" : "vl2_Wire3", + "distance" : 20.0 + }, { + "id" : "vl1_Wire32_ARROW1", + "wireId" : "vl1_Wire32", + "distance" : 20.0 + }, { + "id" : "vl2_Wire11_ARROW2", + "wireId" : "vl2_Wire11", + "distance" : 20.0 + }, { + "id" : "vl1_Wire11_ARROW1", + "wireId" : "vl1_Wire11", + "distance" : 20.0 + }, { + "id" : "vl1_Wire13_ARROW1", + "wireId" : "vl1_Wire13", + "distance" : 20.0 + }, { + "id" : "vl1_Wire15_ARROW1", + "wireId" : "vl1_Wire15", + "distance" : 20.0 + }, { + "id" : "vl1_Wire17_ARROW2", + "wireId" : "vl1_Wire17", + "distance" : 20.0 + }, { + "id" : "vl1_Wire19_ARROW2", + "wireId" : "vl1_Wire19", + "distance" : 20.0 + }, { + "id" : "vl1_Wire11_ARROW2", + "wireId" : "vl1_Wire11", + "distance" : 20.0 + }, { + "id" : "vl1_Wire13_ARROW2", + "wireId" : "vl1_Wire13", + "distance" : 20.0 + }, { + "id" : "vl1_Wire15_ARROW2", + "wireId" : "vl1_Wire15", + "distance" : 20.0 + }, { + "id" : "vl1_Wire17_ARROW1", + "wireId" : "vl1_Wire17", + "distance" : 20.0 + }, { + "id" : "vl1_Wire19_ARROW1", + "wireId" : "vl1_Wire19", + "distance" : 20.0 + }, { + "id" : "vl2_Wire19_ARROW1", + "wireId" : "vl2_Wire19", + "distance" : 20.0 + }, { + "id" : "vl3_Wire12_ARROW2", + "wireId" : "vl3_Wire12", + "distance" : 20.0 + }, { + "id" : "vl2_Wire19_ARROW2", + "wireId" : "vl2_Wire19", + "distance" : 20.0 + }, { + "id" : "vl2_Wire24_ARROW2", + "wireId" : "vl2_Wire24", + "distance" : 20.0 + }, { + "id" : "vl2_Wire22_ARROW1", + "wireId" : "vl2_Wire22", + "distance" : 20.0 + }, { + "id" : "vl2_Wire24_ARROW1", + "wireId" : "vl2_Wire24", + "distance" : 20.0 + }, { + "id" : "vl2_Wire13_ARROW1", + "wireId" : "vl2_Wire13", + "distance" : 20.0 + }, { + "id" : "vl2_Wire13_ARROW2", + "wireId" : "vl2_Wire13", + "distance" : 20.0 + }, { + "id" : "vl2_Wire11_ARROW1", + "wireId" : "vl2_Wire11", + "distance" : 20.0 + }, { + "id" : "vl3_Wire8_ARROW2", + "wireId" : "vl3_Wire8", + "distance" : 20.0 + }, { + "id" : "vl3_Wire8_ARROW1", + "wireId" : "vl3_Wire8", + "distance" : 20.0 + }, { + "id" : "vl1_Wire31_ARROW2", + "wireId" : "vl1_Wire31", + "distance" : 20.0 + }, { + "id" : "vl1_Wire31_ARROW1", + "wireId" : "vl1_Wire31", + "distance" : 20.0 + } ], + "layoutParams" : { + "translateX" : 20.0, + "translateY" : 50.0, + "initialXBus" : 0.0, + "initialYBus" : 260.0, + "verticalSpaceBus" : 25.0, + "horizontalBusPadding" : 20.0, + "cellWidth" : 50.0, + "externCellHeight" : 250.0, + "internCellHeight" : 40.0, + "stackHeight" : 30.0, + "showGrid" : false, + "showInternalNodes" : false, + "scaleFactor" : 1.0, + "horizontalSubstationPadding" : 50.0, + "verticalSubstationPadding" : 50.0, + "drawStraightWires" : false, + "horizontalSnakeLinePadding" : 20.0, + "verticalSnakeLinePadding" : 25.0, + "arrowDistance" : 20.0, + "showInductorFor3WT" : false, + "diagramName" : null, + "shiftFeedersPosition" : false, + "scaleShiftFeedersPosition" : 1.0, + "avoidSVGComponentsDuplication" : false + } +} \ No newline at end of file diff --git a/single-line-diagram-core/src/test/resources/topological.svg b/single-line-diagram-core/src/test/resources/topological.svg new file mode 100644 index 000000000..a6a7ba1b4 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/topological.svg @@ -0,0 +1,281 @@ + + + + + + + bbs + + + + + +l + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + vl + + + diff --git a/single-line-diagram-core/src/test/resources/vlDiag.svg b/single-line-diagram-core/src/test/resources/vlDiag.svg new file mode 100644 index 000000000..08350efb8 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/vlDiag.svg @@ -0,0 +1,878 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bbs1 + + + + bbs2 + + + + bbs3 + + + + bbs4 + + + + + +load1 + + + + + +gen1 + + + + + +load2 + + + + + +gen2 + + + + +trf1 + + + + +trf2 + + + + +trf3 + + + + +trf4 + + + + +trf5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + trf6_ONE_TWO + + + + + +self4_trf6 + + + + + + + + trf7_ONE_TWO + + + + + +self4_trf7 + + + + + + + + trf8_ONE_TWO + + + + + +self4_trf8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +10 + + + + + + + + +10 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + +0 + + + + + + + + +0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vl1 + + + diff --git a/single-line-diagram-core/src/test/resources/vlDiag_metadata.json b/single-line-diagram-core/src/test/resources/vlDiag_metadata.json new file mode 100644 index 000000000..88946dd39 --- /dev/null +++ b/single-line-diagram-core/src/test/resources/vlDiag_metadata.json @@ -0,0 +1,1402 @@ +{ + "components" : [ { + "type" : "BUSBAR_SECTION", + "id" : "bbs4", + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 30.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 70.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 110.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 150.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 190.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 230.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 270.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 310.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 350.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 390.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 430.0, + "y" : 0.0, + "orientation" : "VERTICAL" + }, { + "x" : 460.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + } ], + "size" : { + "width" : 0.0, + "height" : 0.0 + } + }, { + "type" : "GENERATOR", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : -6.0, + "orientation" : "VERTICAL" + }, { + "x" : 0.0, + "y" : 6.0, + "orientation" : "VERTICAL" + }, { + "x" : -6.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 6.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + } ], + "size" : { + "width" : 12.0, + "height" : 12.0 + } + }, { + "type" : "ARROW", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 10.0, + "height" : 10.0 + } + }, { + "type" : "LOAD", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : -4.0, + "orientation" : "VERTICAL" + }, { + "x" : 0.0, + "y" : 4.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 16.0, + "height" : 9.0 + } + }, { + "type" : "NODE", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "NONE" + } ], + "size" : { + "width" : 8.0, + "height" : 8.0 + } + }, { + "type" : "DISCONNECTOR", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 8.0, + "height" : 8.0 + } + }, { + "type" : "LINE", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : 0.0, + "orientation" : "NONE" + } ], + "size" : { + "width" : 0.0, + "height" : 0.0 + } + }, { + "type" : "INDUCTOR", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : -4.0, + "orientation" : "VERTICAL" + }, { + "x" : 0.0, + "y" : 4.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 15.0, + "height" : 9.0 + } + }, { + "type" : "TWO_WINDINGS_TRANSFORMER", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : -4.0, + "orientation" : "VERTICAL" + }, { + "x" : 0.0, + "y" : 4.0, + "orientation" : "VERTICAL" + }, { + "x" : -6.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 6.0, + "y" : 0.0, + "orientation" : "HORIZONTAL" + } ], + "size" : { + "width" : 13.0, + "height" : 8.0 + } + }, { + "type" : "THREE_WINDINGS_TRANSFORMER", + "id" : null, + "anchorPoints" : [ { + "x" : -7.0, + "y" : -4.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 7.0, + "y" : -4.0, + "orientation" : "HORIZONTAL" + }, { + "x" : 0.0, + "y" : 6.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 14.0, + "height" : 12.0 + } + }, { + "type" : "BREAKER", + "id" : null, + "anchorPoints" : [ { + "x" : 0.0, + "y" : -10.0, + "orientation" : "VERTICAL" + }, { + "x" : 0.0, + "y" : 10.0, + "orientation" : "VERTICAL" + } ], + "size" : { + "width" : 20.0, + "height" : 20.0 + } + } ], + "nodes" : [ { + "id" : "dtrct21", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dsect11Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dsect21Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bload1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf3_ONE", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "bload2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf15Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf12Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf2_ONE", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl1_dload2Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf5_ONE", + "vid" : "vl1", + "nextVId" : "vl3", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf16Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_trf81_fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "load2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "LOAD", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "load1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "LOAD", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf13Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_trf61_fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dgen2Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf4_ONE", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "trf6_ONE_TWO", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "FICT_vl1_dsect22Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf8_ONE_TWO", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "dsect21", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dsect22", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs3", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs4", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "gen1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "GENERATOR", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "bgen2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "gen2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "GENERATOR", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "FICT_vl1_dgen1Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf15", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf16", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bgen1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf17", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf18", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf11", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf12", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf13", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf17Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf14Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dtrf14", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "self4_trf6", + "vid" : "vl1", + "nextVId" : "vl3", + "componentType" : "INDUCTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "dsect12", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dload2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "self4_trf8", + "vid" : "vl1", + "nextVId" : "vl3", + "componentType" : "INDUCTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "self4_trf7", + "vid" : "vl1", + "nextVId" : "vl3", + "componentType" : "INDUCTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + }, { + "id" : "dtrct11", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dload1Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "LABEL_VL_vl1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : null, + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : true + }, { + "id" : "FICT_vl1_trf71_fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "THREE_WINDINGS_TRANSFORMER", + "rotationAngle" : 180.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf1_ONE", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "TWO_WINDINGS_TRANSFORMER", + "rotationAngle" : null, + "open" : false, + "direction" : "TOP", + "vlabel" : false + }, { + "id" : "GRID_vl1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : null, + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dsect11", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dload1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf11Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "bbs2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BUSBAR_SECTION", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dgen1", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf11", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf12", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "dgen2", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "DISCONNECTOR", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf15", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dsect12Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : 90.0, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf16", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf13", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf14", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "FICT_vl1_dtrf18Fictif", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "NODE", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf17", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "btrf18", + "vid" : "vl1", + "nextVId" : null, + "componentType" : "BREAKER", + "rotationAngle" : null, + "open" : false, + "direction" : "UNDEFINED", + "vlabel" : false + }, { + "id" : "trf7_ONE_TWO", + "vid" : "vl1", + "nextVId" : "vl2", + "componentType" : "LINE", + "rotationAngle" : null, + "open" : false, + "direction" : "BOTTOM", + "vlabel" : false + } ], + "wires" : [ { + "id" : "vl1_Wire9", + "nodeId1" : "load2", + "nodeId2" : "bload2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire29", + "nodeId1" : "FICT_vl1_trf71_fictif", + "nodeId2" : "trf7_ONE_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire62", + "nodeId1" : "bgen2", + "nodeId2" : "FICT_vl1_dgen2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire61", + "nodeId1" : "dsect22", + "nodeId2" : "FICT_vl1_dsect22Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire20", + "nodeId1" : "bbs1", + "nodeId2" : "dtrf15", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire64", + "nodeId1" : "btrf14", + "nodeId2" : "FICT_vl1_dtrf14Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire63", + "nodeId1" : "dgen2", + "nodeId2" : "FICT_vl1_dgen2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire0", + "nodeId1" : "bbs1", + "nodeId2" : "dsect11", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire60", + "nodeId1" : "dtrct21", + "nodeId2" : "FICT_vl1_dsect22Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire4", + "nodeId1" : "bbs1", + "nodeId2" : "dload1", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire26", + "nodeId1" : "FICT_vl1_trf61_fictif", + "nodeId2" : "trf6_ONE_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire3", + "nodeId1" : "dsect22", + "nodeId2" : "bbs4", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire25", + "nodeId1" : "btrf16", + "nodeId2" : "FICT_vl1_trf61_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire2", + "nodeId1" : "bbs3", + "nodeId2" : "dsect21", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire28", + "nodeId1" : "btrf17", + "nodeId2" : "FICT_vl1_trf71_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire1", + "nodeId1" : "dsect12", + "nodeId2" : "bbs2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire27", + "nodeId1" : "FICT_vl1_trf61_fictif", + "nodeId2" : "self4_trf6", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire8", + "nodeId1" : "bbs2", + "nodeId2" : "dload2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire22", + "nodeId1" : "bbs1", + "nodeId2" : "dtrf16", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire7", + "nodeId1" : "gen1", + "nodeId2" : "bgen1", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire21", + "nodeId1" : "btrf15", + "nodeId2" : "trf5_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire65", + "nodeId1" : "dtrf14", + "nodeId2" : "FICT_vl1_dtrf14Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire6", + "nodeId1" : "bbs3", + "nodeId2" : "dgen1", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire24", + "nodeId1" : "bbs2", + "nodeId2" : "dtrf18", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire5", + "nodeId1" : "load1", + "nodeId2" : "bload1", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire23", + "nodeId1" : "bbs3", + "nodeId2" : "dtrf17", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire19", + "nodeId1" : "btrf14", + "nodeId2" : "trf4_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire18", + "nodeId1" : "bbs4", + "nodeId2" : "dtrf14", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire51", + "nodeId1" : "dtrf18", + "nodeId2" : "FICT_vl1_dtrf18Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire50", + "nodeId1" : "btrf18", + "nodeId2" : "FICT_vl1_dtrf18Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire53", + "nodeId1" : "dsect21", + "nodeId2" : "FICT_vl1_dsect21Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire52", + "nodeId1" : "dtrct21", + "nodeId2" : "FICT_vl1_dsect21Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire15", + "nodeId1" : "btrf12", + "nodeId2" : "trf2_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire59", + "nodeId1" : "dtrf17", + "nodeId2" : "FICT_vl1_dtrf17Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire14", + "nodeId1" : "bbs2", + "nodeId2" : "dtrf12", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire58", + "nodeId1" : "btrf17", + "nodeId2" : "FICT_vl1_dtrf17Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire17", + "nodeId1" : "btrf13", + "nodeId2" : "trf3_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire16", + "nodeId1" : "bbs3", + "nodeId2" : "dtrf13", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire11", + "nodeId1" : "gen2", + "nodeId2" : "bgen2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire55", + "nodeId1" : "dgen1", + "nodeId2" : "FICT_vl1_dgen1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire10", + "nodeId1" : "bbs4", + "nodeId2" : "dgen2", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire54", + "nodeId1" : "bgen1", + "nodeId2" : "FICT_vl1_dgen1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire13", + "nodeId1" : "btrf11", + "nodeId2" : "trf1_ONE", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire57", + "nodeId1" : "dtrf13", + "nodeId2" : "FICT_vl1_dtrf13Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire12", + "nodeId1" : "bbs1", + "nodeId2" : "dtrf11", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire56", + "nodeId1" : "btrf13", + "nodeId2" : "FICT_vl1_dtrf13Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire40", + "nodeId1" : "btrf15", + "nodeId2" : "FICT_vl1_dtrf15Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire42", + "nodeId1" : "btrf16", + "nodeId2" : "FICT_vl1_dtrf16Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire41", + "nodeId1" : "dtrf15", + "nodeId2" : "FICT_vl1_dtrf15Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire48", + "nodeId1" : "btrf12", + "nodeId2" : "FICT_vl1_dtrf12Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire47", + "nodeId1" : "dload2", + "nodeId2" : "FICT_vl1_dload2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire49", + "nodeId1" : "dtrf12", + "nodeId2" : "FICT_vl1_dtrf12Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire44", + "nodeId1" : "dtrct11", + "nodeId2" : "FICT_vl1_dsect12Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire43", + "nodeId1" : "dtrf16", + "nodeId2" : "FICT_vl1_dtrf16Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire46", + "nodeId1" : "bload2", + "nodeId2" : "FICT_vl1_dload2Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire45", + "nodeId1" : "dsect12", + "nodeId2" : "FICT_vl1_dsect12Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire31", + "nodeId1" : "btrf18", + "nodeId2" : "FICT_vl1_trf81_fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire30", + "nodeId1" : "FICT_vl1_trf71_fictif", + "nodeId2" : "self4_trf7", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire37", + "nodeId1" : "dload1", + "nodeId2" : "FICT_vl1_dload1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire36", + "nodeId1" : "bload1", + "nodeId2" : "FICT_vl1_dload1Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire39", + "nodeId1" : "dtrf11", + "nodeId2" : "FICT_vl1_dtrf11Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire38", + "nodeId1" : "btrf11", + "nodeId2" : "FICT_vl1_dtrf11Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire33", + "nodeId1" : "FICT_vl1_trf81_fictif", + "nodeId2" : "self4_trf8", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire32", + "nodeId1" : "FICT_vl1_trf81_fictif", + "nodeId2" : "trf8_ONE_TWO", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire35", + "nodeId1" : "dsect11", + "nodeId2" : "FICT_vl1_dsect11Fictif", + "straight" : false, + "snakeLine" : false + }, { + "id" : "vl1_Wire34", + "nodeId1" : "dtrct11", + "nodeId2" : "FICT_vl1_dsect11Fictif", + "straight" : false, + "snakeLine" : false + } ], + "arrows" : [ { + "id" : "vl1_Wire26_ARROW1", + "wireId" : "vl1_Wire26", + "distance" : 20.0 + }, { + "id" : "vl1_Wire26_ARROW2", + "wireId" : "vl1_Wire26", + "distance" : 20.0 + }, { + "id" : "vl1_Wire27_ARROW2", + "wireId" : "vl1_Wire27", + "distance" : 20.0 + }, { + "id" : "vl1_Wire27_ARROW1", + "wireId" : "vl1_Wire27", + "distance" : 20.0 + }, { + "id" : "vl1_Wire33_ARROW2", + "wireId" : "vl1_Wire33", + "distance" : 20.0 + }, { + "id" : "vl1_Wire32_ARROW1", + "wireId" : "vl1_Wire32", + "distance" : 20.0 + }, { + "id" : "vl1_Wire33_ARROW1", + "wireId" : "vl1_Wire33", + "distance" : 20.0 + }, { + "id" : "vl1_Wire29_ARROW2", + "wireId" : "vl1_Wire29", + "distance" : 20.0 + }, { + "id" : "vl1_Wire29_ARROW1", + "wireId" : "vl1_Wire29", + "distance" : 20.0 + }, { + "id" : "vl1_Wire21_ARROW1", + "wireId" : "vl1_Wire21", + "distance" : 20.0 + }, { + "id" : "vl1_Wire11_ARROW1", + "wireId" : "vl1_Wire11", + "distance" : 20.0 + }, { + "id" : "vl1_Wire13_ARROW1", + "wireId" : "vl1_Wire13", + "distance" : 20.0 + }, { + "id" : "vl1_Wire15_ARROW1", + "wireId" : "vl1_Wire15", + "distance" : 20.0 + }, { + "id" : "vl1_Wire17_ARROW2", + "wireId" : "vl1_Wire17", + "distance" : 20.0 + }, { + "id" : "vl1_Wire19_ARROW2", + "wireId" : "vl1_Wire19", + "distance" : 20.0 + }, { + "id" : "vl1_Wire11_ARROW2", + "wireId" : "vl1_Wire11", + "distance" : 20.0 + }, { + "id" : "vl1_Wire13_ARROW2", + "wireId" : "vl1_Wire13", + "distance" : 20.0 + }, { + "id" : "vl1_Wire15_ARROW2", + "wireId" : "vl1_Wire15", + "distance" : 20.0 + }, { + "id" : "vl1_Wire17_ARROW1", + "wireId" : "vl1_Wire17", + "distance" : 20.0 + }, { + "id" : "vl1_Wire19_ARROW1", + "wireId" : "vl1_Wire19", + "distance" : 20.0 + }, { + "id" : "vl1_Wire9_ARROW2", + "wireId" : "vl1_Wire9", + "distance" : 20.0 + }, { + "id" : "vl1_Wire21_ARROW2", + "wireId" : "vl1_Wire21", + "distance" : 20.0 + }, { + "id" : "vl1_Wire30_ARROW2", + "wireId" : "vl1_Wire30", + "distance" : 20.0 + }, { + "id" : "vl1_Wire30_ARROW1", + "wireId" : "vl1_Wire30", + "distance" : 20.0 + }, { + "id" : "vl1_Wire5_ARROW2", + "wireId" : "vl1_Wire5", + "distance" : 20.0 + }, { + "id" : "vl1_Wire32_ARROW2", + "wireId" : "vl1_Wire32", + "distance" : 20.0 + }, { + "id" : "vl1_Wire7_ARROW2", + "wireId" : "vl1_Wire7", + "distance" : 20.0 + }, { + "id" : "vl1_Wire5_ARROW1", + "wireId" : "vl1_Wire5", + "distance" : 20.0 + }, { + "id" : "vl1_Wire7_ARROW1", + "wireId" : "vl1_Wire7", + "distance" : 20.0 + }, { + "id" : "vl1_Wire9_ARROW1", + "wireId" : "vl1_Wire9", + "distance" : 20.0 + } ], + "layoutParams" : { + "translateX" : 20.0, + "translateY" : 50.0, + "initialXBus" : 0.0, + "initialYBus" : 260.0, + "verticalSpaceBus" : 25.0, + "horizontalBusPadding" : 20.0, + "cellWidth" : 50.0, + "externCellHeight" : 250.0, + "internCellHeight" : 40.0, + "stackHeight" : 30.0, + "showGrid" : false, + "showInternalNodes" : false, + "scaleFactor" : 1.0, + "horizontalSubstationPadding" : 50.0, + "verticalSubstationPadding" : 50.0, + "drawStraightWires" : false, + "horizontalSnakeLinePadding" : 20.0, + "verticalSnakeLinePadding" : 25.0, + "arrowDistance" : 20.0, + "showInductorFor3WT" : false, + "diagramName" : null, + "shiftFeedersPosition" : false, + "scaleShiftFeedersPosition" : 1.0, + "avoidSVGComponentsDuplication" : false + } +} \ No newline at end of file diff --git a/single-line-diagram-iidm-extensions/pom.xml b/single-line-diagram-iidm-extensions/pom.xml new file mode 100644 index 000000000..079068b92 --- /dev/null +++ b/single-line-diagram-iidm-extensions/pom.xml @@ -0,0 +1,77 @@ + + + + 4.0.0 + + + com.powsybl + powsybl-single-line-diagram + 1.0.0-SNAPSHOT + + + powsybl-single-line-diagram-iidm-extensions + Single line diagram IIDM extensions + + + + + com.powsybl + powsybl-iidm-api + + + + + com.google.jimfs + jimfs + test + + + junit + junit + test + + + org.slf4j + slf4j-simple + test + + + org.xmlunit + xmlunit-core + test + + + + com.powsybl + powsybl-commons + ${powsyblcore.version} + test-jar + test + + + com.powsybl + powsybl-iidm-impl + test + + + com.powsybl + powsybl-iidm-xml-converter + test + + + com.powsybl + powsybl-iidm-test + test + + + + \ No newline at end of file diff --git a/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/BusbarSectionPosition.java b/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/BusbarSectionPosition.java new file mode 100644 index 000000000..c15d46a61 --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/BusbarSectionPosition.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.iidm.extensions; + +import com.powsybl.commons.extensions.AbstractExtension; +import com.powsybl.iidm.network.BusbarSection; + +/** + * @author Geoffroy Jamgotchian + */ +public class BusbarSectionPosition extends AbstractExtension { + + private int busbarIndex; + + private int sectionIndex; + + private static int checkIndex(int index) { + if (index < 0) { + throw new IllegalArgumentException("Busbar index has to be greater or equals to zero"); + } + return index; + } + + public BusbarSectionPosition(BusbarSection busbarSection, int busbarIndex, int sectionIndex) { + super(busbarSection); + this.busbarIndex = checkIndex(busbarIndex); + this.sectionIndex = checkIndex(sectionIndex); + } + + @Override + public String getName() { + return "busbarSectionPosition"; + } + + public int getBusbarIndex() { + return busbarIndex; + } + + public void setBusbarIndex(int busbarIndex) { + this.busbarIndex = checkIndex(busbarIndex); + } + + public int getSectionIndex() { + return sectionIndex; + } + + public void setSectionIndex(int sectionIndex) { + this.sectionIndex = checkIndex(sectionIndex); + } +} diff --git a/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/BusbarSectionPositionXmlSerializer.java b/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/BusbarSectionPositionXmlSerializer.java new file mode 100644 index 000000000..f855fb32a --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/BusbarSectionPositionXmlSerializer.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.iidm.extensions; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.extensions.ExtensionXmlSerializer; +import com.powsybl.commons.xml.XmlReaderContext; +import com.powsybl.commons.xml.XmlUtil; +import com.powsybl.commons.xml.XmlWriterContext; +import com.powsybl.iidm.network.BusbarSection; + +import javax.xml.stream.XMLStreamException; +import java.io.InputStream; + +/** + * @author Geoffroy Jamgotchian + */ +@AutoService(ExtensionXmlSerializer.class) +public class BusbarSectionPositionXmlSerializer implements ExtensionXmlSerializer { + + @Override + public String getExtensionName() { + return "busbarSectionPosition"; + } + + @Override + public String getCategoryName() { + return "network"; + } + + @Override + public Class getExtensionClass() { + return BusbarSectionPosition.class; + } + + @Override + public boolean hasSubElements() { + return false; + } + + @Override + public InputStream getXsdAsStream() { + return getClass().getResourceAsStream("/xsd/busbarSectionPosition.xsd"); + } + + @Override + public String getNamespaceUri() { + return "http://www.itesla_project.eu/schema/iidm/ext/busbarsectionposition/1_0"; + } + + @Override + public String getNamespacePrefix() { + return "bbsp"; + } + + @Override + public void write(BusbarSectionPosition busbarSectionPosition, XmlWriterContext context) throws XMLStreamException { + XmlUtil.writeInt("busbarIndex", busbarSectionPosition.getBusbarIndex(), context.getExtensionsWriter()); + XmlUtil.writeInt("sectionIndex", busbarSectionPosition.getSectionIndex(), context.getExtensionsWriter()); + } + + @Override + public BusbarSectionPosition read(BusbarSection busbarSection, XmlReaderContext context) { + int busbarIndex = XmlUtil.readIntAttribute(context.getReader(), "busbarIndex"); + int sectionIndex = XmlUtil.readIntAttribute(context.getReader(), "sectionIndex"); + return new BusbarSectionPosition(busbarSection, busbarIndex, sectionIndex); + } +} diff --git a/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/ConnectablePosition.java b/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/ConnectablePosition.java new file mode 100644 index 000000000..19b8aa104 --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/ConnectablePosition.java @@ -0,0 +1,134 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.iidm.extensions; + +import com.powsybl.commons.extensions.AbstractExtension; +import com.powsybl.iidm.network.Connectable; + +import java.util.Objects; + +/** + * @author Geoffroy Jamgotchian + */ +public class ConnectablePosition> extends AbstractExtension { + + static final String NAME = "position"; + + public enum Direction { + TOP, + BOTTOM, + UNDEFINED + } + + public static class Feeder { + + private String name; + + private int order; + + private Direction direction; + + public Feeder(String name, int order, Direction direction) { + this.name = Objects.requireNonNull(name); + this.order = order; + this.direction = Objects.requireNonNull(direction); + } + + public String getName() { + return name; + } + + public Feeder setName(String name) { + this.name = Objects.requireNonNull(name); + return this; + } + + public int getOrder() { + return order; + } + + public Feeder setOrder(int order) { + this.order = order; + return this; + } + + public Direction getDirection() { + return direction; + } + + public Feeder setDirection(Direction direction) { + this.direction = Objects.requireNonNull(direction); + return this; + } + } + + private Feeder feeder; + private Feeder feeder1; + private Feeder feeder2; + private Feeder feeder3; + + private static void check(Feeder feeder, Feeder feeder1, Feeder feeder2, Feeder feeder3) { + if (feeder == null && feeder1 == null && feeder2 == null && feeder3 == null) { + throw new IllegalArgumentException("invalid feeder"); + } + if (feeder != null && (feeder1 != null || feeder2 != null || feeder3 != null)) { + throw new IllegalArgumentException("feeder and feeder 1|2|3 are exclusives"); + } + boolean error = false; + if (feeder1 != null) { + if (feeder2 == null && feeder3 != null) { + error = true; + } + } else { + if (feeder2 != null || feeder3 != null) { + error = true; + } + } + if (error) { + throw new IllegalArgumentException("feeder 1|2|3 have to be set in the right order"); + } + } + + public ConnectablePosition(C connectable, Feeder feeder, Feeder feeder1, Feeder feeder2, Feeder feeder3) { + super(connectable); + check(feeder, feeder1, feeder2, feeder3); + this.feeder = feeder; + this.feeder1 = feeder1; + this.feeder2 = feeder2; + this.feeder3 = feeder3; + } + + @Override + public String getName() { + return NAME; + } + + public Feeder getFeeder() { + return feeder; + } + + public Feeder getFeeder1() { + return feeder1; + } + + public Feeder getFeeder2() { + return feeder2; + } + + public Feeder getFeeder3() { + return feeder3; + } + + public ConnectablePosition setFeeders(Feeder feeder, Feeder feeder1, Feeder feeder2, Feeder feeder3) { + check(feeder, feeder1, feeder2, feeder3); + this.feeder = feeder; + this.feeder1 = feeder1; + this.feeder2 = feeder2; + this.feeder3 = feeder3; + return this; + } +} diff --git a/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/ConnectablePositionXmlSerializer.java b/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/ConnectablePositionXmlSerializer.java new file mode 100644 index 000000000..78f16dea3 --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/main/java/com/powsybl/sld/iidm/extensions/ConnectablePositionXmlSerializer.java @@ -0,0 +1,121 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.iidm.extensions; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.extensions.ExtensionXmlSerializer; +import com.powsybl.commons.xml.XmlReaderContext; +import com.powsybl.commons.xml.XmlUtil; +import com.powsybl.commons.xml.XmlWriterContext; +import com.powsybl.iidm.network.Connectable; + +import javax.xml.stream.XMLStreamException; +import java.io.InputStream; + +/** + * @author Geoffroy Jamgotchian + */ +@AutoService(ExtensionXmlSerializer.class) +public class ConnectablePositionXmlSerializer> implements ExtensionXmlSerializer> { + + @Override + public String getExtensionName() { + return ConnectablePosition.NAME; + } + + @Override + public String getCategoryName() { + return "network"; + } + + @Override + public Class getExtensionClass() { + return ConnectablePosition.class; + } + + @Override + public boolean hasSubElements() { + return true; + } + + @Override + public InputStream getXsdAsStream() { + return getClass().getResourceAsStream("/xsd/connectablePosition.xsd"); + } + + @Override + public String getNamespaceUri() { + return "http://www.itesla_project.eu/schema/iidm/ext/connectable_position/1_0"; + } + + @Override + public String getNamespacePrefix() { + return "cp"; + } + + private void writePosition(ConnectablePosition.Feeder feeder, Integer i, XmlWriterContext context) throws XMLStreamException { + context.getExtensionsWriter().writeEmptyElement(getNamespaceUri(), "feeder" + (i != null ? i : "")); + context.getExtensionsWriter().writeAttribute("name", feeder.getName()); + XmlUtil.writeInt("order", feeder.getOrder(), context.getExtensionsWriter()); + context.getExtensionsWriter().writeAttribute("direction", feeder.getDirection().name()); + } + + @Override + public void write(ConnectablePosition connectablePosition, XmlWriterContext context) throws XMLStreamException { + if (connectablePosition.getFeeder() != null) { + writePosition(connectablePosition.getFeeder(), null, context); + } + if (connectablePosition.getFeeder1() != null) { + writePosition(connectablePosition.getFeeder1(), 1, context); + } + if (connectablePosition.getFeeder2() != null) { + writePosition(connectablePosition.getFeeder2(), 2, context); + } + if (connectablePosition.getFeeder3() != null) { + writePosition(connectablePosition.getFeeder3(), 3, context); + } + } + + private ConnectablePosition.Feeder readPosition(XmlReaderContext context) { + String name = context.getReader().getAttributeValue(null, "name"); + int order = XmlUtil.readIntAttribute(context.getReader(), "order"); + ConnectablePosition.Direction direction = ConnectablePosition.Direction.valueOf(context.getReader().getAttributeValue(null, "direction")); + return new ConnectablePosition.Feeder(name, order, direction); + } + + @Override + public ConnectablePosition read(Connectable connectable, XmlReaderContext context) throws XMLStreamException { + ConnectablePosition.Feeder[] feeder = new ConnectablePosition.Feeder[1]; + ConnectablePosition.Feeder[] feeder1 = new ConnectablePosition.Feeder[1]; + ConnectablePosition.Feeder[] feeder2 = new ConnectablePosition.Feeder[1]; + ConnectablePosition.Feeder[] feeder3 = new ConnectablePosition.Feeder[1]; + XmlUtil.readUntilEndElement(getExtensionName(), context.getReader(), () -> { + + switch (context.getReader().getLocalName()) { + case "feeder": + feeder[0] = readPosition(context); + break; + + case "feeder1": + feeder1[0] = readPosition(context); + break; + + case "feeder2": + feeder2[0] = readPosition(context); + break; + + case "feeder3": + feeder3[0] = readPosition(context); + break; + + default: + throw new AssertionError(); + } + }); + return new ConnectablePosition(connectable, feeder[0], feeder1[0], feeder2[0], feeder3[0]); + } +} diff --git a/single-line-diagram-iidm-extensions/src/main/resources/xsd/busbarSectionPosition.xsd b/single-line-diagram-iidm-extensions/src/main/resources/xsd/busbarSectionPosition.xsd new file mode 100644 index 000000000..6b0cb2226 --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/main/resources/xsd/busbarSectionPosition.xsd @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/single-line-diagram-iidm-extensions/src/main/resources/xsd/connectablePosition.xsd b/single-line-diagram-iidm-extensions/src/main/resources/xsd/connectablePosition.xsd new file mode 100644 index 000000000..6964a90f1 --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/main/resources/xsd/connectablePosition.xsd @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/single-line-diagram-iidm-extensions/src/test/java/com/powsybl/sld/iidm/extensions/BusbarSectionPositionXmlTest.java b/single-line-diagram-iidm-extensions/src/test/java/com/powsybl/sld/iidm/extensions/BusbarSectionPositionXmlTest.java new file mode 100644 index 000000000..e19885a70 --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/test/java/com/powsybl/sld/iidm/extensions/BusbarSectionPositionXmlTest.java @@ -0,0 +1,65 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.iidm.extensions; + +import com.powsybl.commons.AbstractConverterTest; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.xml.NetworkXml; +import org.joda.time.DateTime; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Geoffroy Jamgotchian + */ +public class BusbarSectionPositionXmlTest extends AbstractConverterTest { + + private static Network createTestNetwork() { + Network network = Network.create("test", "test"); + network.setCaseDate(DateTime.parse("2016-06-27T12:27:58.535+02:00")); + Substation s = network.newSubstation() + .setId("S") + .setCountry(Country.FR) + .add(); + VoltageLevel vl = s.newVoltageLevel() + .setId("VL") + .setNominalV(400) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + vl.getNodeBreakerView().setNodeCount(1); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS") + .setNode(0) + .add(); + return network; + } + + @Test + public void test() throws IOException { + Network network = createTestNetwork(); + + // extends busbar section + BusbarSection busbarSection = network.getVoltageLevel("VL").getNodeBreakerView().getBusbarSection("BBS"); + BusbarSectionPosition busbarSectionPosition = new BusbarSectionPosition(busbarSection, 0, 1); + busbarSection.addExtension(BusbarSectionPosition.class, busbarSectionPosition); + + Network network2 = roundTripXmlTest(network, + NetworkXml::writeAndValidate, + NetworkXml::read, + "/busbarSectionPositionRef.xml"); + + BusbarSection busbarSection2 = network2.getVoltageLevel("VL").getNodeBreakerView().getBusbarSection("BBS"); + BusbarSectionPosition busbarSectionPosition2 = busbarSection2.getExtension(BusbarSectionPosition.class); + assertNotNull(busbarSectionPosition2); + assertEquals(busbarSectionPosition.getBusbarIndex(), busbarSectionPosition2.getBusbarIndex()); + assertEquals(busbarSectionPosition.getSectionIndex(), busbarSectionPosition2.getSectionIndex()); + } +} diff --git a/single-line-diagram-iidm-extensions/src/test/java/com/powsybl/sld/iidm/extensions/ConnectablePositionXmlTest.java b/single-line-diagram-iidm-extensions/src/test/java/com/powsybl/sld/iidm/extensions/ConnectablePositionXmlTest.java new file mode 100644 index 000000000..4da0983e8 --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/test/java/com/powsybl/sld/iidm/extensions/ConnectablePositionXmlTest.java @@ -0,0 +1,136 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.iidm.extensions; + +import com.powsybl.commons.AbstractConverterTest; +import com.powsybl.iidm.network.*; +import com.powsybl.iidm.xml.NetworkXml; +import org.joda.time.DateTime; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +/** + * @author Geoffroy Jamgotchian + */ +public class ConnectablePositionXmlTest extends AbstractConverterTest { + + private static Network createTestNetwork() { + Network network = Network.create("test", "test"); + network.setCaseDate(DateTime.parse("2016-06-27T12:27:58.535+02:00")); + Substation s = network.newSubstation() + .setId("S") + .setCountry(Country.FR) + .add(); + VoltageLevel vl = s.newVoltageLevel() + .setId("VL") + .setNominalV(400) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + vl.getNodeBreakerView().setNodeCount(3); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS") + .setNode(0) + .add(); + vl.newGenerator() + .setId("G") + .setNode(1) + .setEnergySource(EnergySource.NUCLEAR) + .setMinP(10) + .setMaxP(20) + .setTargetP(20) + .setTargetV(400) + .setVoltageRegulatorOn(true) + .add(); + vl.getNodeBreakerView().newBreaker() + .setId("SW") + .setNode1(0) + .setNode2(1) + .setOpen(false) + .add(); + Substation s2 = network.newSubstation() + .setId("S2") + .setCountry(Country.FR) + .add(); + VoltageLevel vl2 = s2.newVoltageLevel() + .setId("VL2") + .setNominalV(400) + .setTopologyKind(TopologyKind.NODE_BREAKER) + .add(); + vl2.getNodeBreakerView().setNodeCount(1); + network.newLine() + .setId("L") + .setVoltageLevel1("VL") + .setNode1(2) + .setVoltageLevel2("VL2") + .setNode2(0) + .setR(1) + .setX(1) + .setG1(0) + .setG2(0) + .setB1(0) + .setB2(0) + .add(); + return network; + } + + @Test + public void test() throws IOException { + Network network = createTestNetwork(); + + // extend generator + Generator generator = network.getGenerator("G"); + assertNotNull(generator); + ConnectablePosition generationPosition = new ConnectablePosition<>(generator, + new ConnectablePosition.Feeder("G", 10, ConnectablePosition.Direction.TOP), + null, + null, + null); + generator.addExtension(ConnectablePosition.class, generationPosition); + + // extend line + Line line = network.getLine("L"); + assertNotNull(line); + ConnectablePosition linePosition = new ConnectablePosition<>(line, + null, + new ConnectablePosition.Feeder("L", 10, ConnectablePosition.Direction.TOP), + new ConnectablePosition.Feeder("L", 20, ConnectablePosition.Direction.BOTTOM), + null); + line.addExtension(ConnectablePosition.class, linePosition); + + Network network2 = roundTripXmlTest(network, + NetworkXml::writeAndValidate, + NetworkXml::read, + "/connectablePositionRef.xml"); + + Generator generator2 = network2.getGenerator("G"); + assertNotNull(generator); + ConnectablePosition generatorPosition2 = generator2.getExtension(ConnectablePosition.class); + assertNotNull(generatorPosition2); + assertNotNull(generatorPosition2.getFeeder()); + assertNull(generatorPosition2.getFeeder1()); + assertNull(generatorPosition2.getFeeder2()); + assertNull(generatorPosition2.getFeeder3()); + assertEquals(generatorPosition2.getFeeder().getOrder(), generationPosition.getFeeder().getOrder()); + assertEquals(generatorPosition2.getFeeder().getDirection(), generationPosition.getFeeder().getDirection()); + + Line line2 = network2.getLine("L"); + assertNotNull(line2); + ConnectablePosition linePosition2 = line2.getExtension(ConnectablePosition.class); + assertNotNull(linePosition2); + assertNull(linePosition2.getFeeder()); + assertNotNull(linePosition2.getFeeder1()); + assertNotNull(linePosition2.getFeeder2()); + assertNull(linePosition2.getFeeder3()); + assertEquals(linePosition2.getFeeder1().getOrder(), linePosition2.getFeeder1().getOrder()); + assertEquals(linePosition2.getFeeder1().getDirection(), linePosition2.getFeeder1().getDirection()); + assertEquals(linePosition2.getFeeder2().getOrder(), linePosition2.getFeeder2().getOrder()); + assertEquals(linePosition2.getFeeder2().getDirection(), linePosition2.getFeeder2().getDirection()); + } +} diff --git a/single-line-diagram-iidm-extensions/src/test/resources/busbarSectionPositionRef.xml b/single-line-diagram-iidm-extensions/src/test/resources/busbarSectionPositionRef.xml new file mode 100644 index 000000000..a9a948307 --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/test/resources/busbarSectionPositionRef.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/single-line-diagram-iidm-extensions/src/test/resources/connectablePositionRef.xml b/single-line-diagram-iidm-extensions/src/test/resources/connectablePositionRef.xml new file mode 100644 index 000000000..ef991f3b3 --- /dev/null +++ b/single-line-diagram-iidm-extensions/src/test/resources/connectablePositionRef.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/single-line-diagram-util/pom.xml b/single-line-diagram-util/pom.xml new file mode 100644 index 000000000..e48f166da --- /dev/null +++ b/single-line-diagram-util/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + + powsybl-single-line-diagram + com.powsybl + 1.0.0-SNAPSHOT + + + powsybl-single-line-diagram-util + Single line diagram utilities + + + + + com.powsybl + powsybl-single-line-diagram-core + ${project.version} + + + com.powsybl + powsybl-single-line-diagram-cgmes + ${project.version} + + + com.powsybl + powsybl-cgmes-dl-conversion + ${project.version} + + + + + junit + junit + test + + + org.slf4j + slf4j-simple + test + + + com.google.jimfs + jimfs + test + + + diff --git a/single-line-diagram-util/src/main/java/com/powsybl/sld/util/SmartVoltageLevelLayoutFactory.java b/single-line-diagram-util/src/main/java/com/powsybl/sld/util/SmartVoltageLevelLayoutFactory.java new file mode 100644 index 000000000..0f2d8993f --- /dev/null +++ b/single-line-diagram-util/src/main/java/com/powsybl/sld/util/SmartVoltageLevelLayoutFactory.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.util; + +import com.powsybl.cgmes.iidm.extensions.dl.CouplingDeviceDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.InjectionDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.LineDiagramData; +import com.powsybl.cgmes.iidm.extensions.dl.NodeDiagramData; +import com.powsybl.iidm.network.Connectable; +import com.powsybl.iidm.network.TopologyKind; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.cgmes.CgmesVoltageLevelLayoutFactory; +import com.powsybl.sld.iidm.extensions.BusbarSectionPosition; +import com.powsybl.sld.iidm.extensions.ConnectablePosition; +import com.powsybl.sld.layout.PositionFree; +import com.powsybl.sld.layout.PositionVoltageLevelLayoutFactory; +import com.powsybl.sld.layout.VoltageLevelLayout; +import com.powsybl.sld.layout.VoltageLevelLayoutFactory; +import com.powsybl.sld.model.Graph; + +/** + * @author Geoffroy Jamgotchian + */ +public class SmartVoltageLevelLayoutFactory implements VoltageLevelLayoutFactory { + + @Override + public VoltageLevelLayout create(Graph graph) { + VoltageLevel vl = graph.getVoltageLevel(); + + if (vl.getTopologyKind() == TopologyKind.BUS_BREAKER) { + // because bus/breaker topology is managed by adding position extensions, we need to use layout with + // extensions + return new PositionVoltageLevelLayoutFactory().create(graph); + } + + // check for position extensions + for (Connectable c : vl.getConnectables()) { + if (c.getExtension(ConnectablePosition.class) != null + || c.getExtension(BusbarSectionPosition.class) != null) { + return new PositionVoltageLevelLayoutFactory().create(graph); + } + } + + // check for cgmes extensions + for (Connectable c : vl.getConnectables()) { + if (c.getExtension(InjectionDiagramData.class) != null + || c.getExtension(LineDiagramData.class) != null + || c.getExtension(NodeDiagramData.class) != null + || c.getExtension(CouplingDeviceDiagramData.class) != null) { + return new CgmesVoltageLevelLayoutFactory().create(graph); + } + } + + return new PositionVoltageLevelLayoutFactory(new PositionFree()).create(graph); + } +} diff --git a/single-line-diagram-util/src/main/java/com/powsybl/sld/util/SubstationDiagramTool.java b/single-line-diagram-util/src/main/java/com/powsybl/sld/util/SubstationDiagramTool.java new file mode 100644 index 000000000..23d4c2e8c --- /dev/null +++ b/single-line-diagram-util/src/main/java/com/powsybl/sld/util/SubstationDiagramTool.java @@ -0,0 +1,187 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.util; + +import com.google.auto.service.AutoService; +import com.powsybl.commons.PowsyblException; +import com.powsybl.iidm.import_.Importers; +import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.Substation; +import com.powsybl.iidm.network.VoltageLevel; +import com.powsybl.sld.SubstationDiagram; +import com.powsybl.sld.VoltageLevelDiagram; +import com.powsybl.sld.layout.HorizontalSubstationLayoutFactory; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.layout.SubstationLayoutFactory; +import com.powsybl.sld.layout.VoltageLevelLayoutFactory; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import com.powsybl.tools.Command; +import com.powsybl.tools.Tool; +import com.powsybl.tools.ToolOptions; +import com.powsybl.tools.ToolRunningContext; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; + +/** + * @author Geoffroy Jamgotchian + */ +@AutoService(Tool.class) +public class SubstationDiagramTool implements Tool { + + private static final String INPUT_FILE = "input-file"; + private static final String OUTPUT_DIR = "output-dir"; + private static final String IDS = "ids"; + private static final String ALL_VOLTAGE_LEVELS = "all-voltage-levels"; + private static final String ALL_SUBSTATIONS = "all-substations"; + + @Override + public Command getCommand() { + return new Command() { + + @Override + public String getName() { + return "generate-substation-diagram"; + } + + @Override + public String getTheme() { + return "Substation diagram"; + } + + @Override + public String getDescription() { + return "generate substation diagram"; + } + + @Override + public Options getOptions() { + Options options = new Options(); + options.addOption(Option.builder().longOpt(INPUT_FILE) + .desc("the input file") + .hasArg() + .argName("INPUT_FILE") + .required() + .build()); + options.addOption(Option.builder().longOpt(OUTPUT_DIR) + .desc("the output directory") + .hasArg() + .argName("OUTPUT_DIR") + .required() + .build()); + options.addOption(Option.builder().longOpt(IDS) + .desc("voltage level/substation id list") + .hasArg() + .argName("ID_LIST") + .build()); + options.addOption(Option.builder().longOpt(ALL_VOLTAGE_LEVELS) + .desc("all voltage levels") + .build()); + options.addOption(Option.builder().longOpt(ALL_SUBSTATIONS) + .desc("all substations") + .build()); + + return options; + } + + @Override + public String getUsageFooter() { + return null; + } + }; + } + + private void generateSvg(ToolRunningContext context, Path outputDir, ComponentLibrary componentLibrary, + LayoutParameters parameters, VoltageLevelLayoutFactory voltageLevelLayoutFactory, VoltageLevel vl, Network network) throws UnsupportedEncodingException { + Path svgFile = outputDir.resolve(URLEncoder.encode(vl.getId(), StandardCharsets.UTF_8.name()) + ".svg"); + context.getOutputStream().println("Generating '" + svgFile + "' (" + vl.getNominalV() + ")"); + try { + VoltageLevelDiagram.build(vl, voltageLevelLayoutFactory, true, parameters.isShowInductorFor3WT()) + .writeSvg("", componentLibrary, parameters, network, svgFile); + } catch (Exception e) { + e.printStackTrace(context.getErrorStream()); + } + } + + private void generateSvg(ToolRunningContext context, Path outputDir, ComponentLibrary componentLibrary, + LayoutParameters parameters, VoltageLevelLayoutFactory voltageLevelLayoutFactory, + SubstationLayoutFactory substationLayoutFactory, + Substation s, Network network) throws UnsupportedEncodingException { + Path svgFile = outputDir.resolve(URLEncoder.encode(s.getId(), StandardCharsets.UTF_8.name()) + ".svg"); + context.getOutputStream().println("Generating '" + svgFile + "'"); + try { + SubstationDiagram.build(s, substationLayoutFactory, voltageLevelLayoutFactory, true) + .writeSvg("", componentLibrary, parameters, network, svgFile); + } catch (Exception e) { + e.printStackTrace(context.getErrorStream()); + } + } + + @Override + public void run(CommandLine line, ToolRunningContext context) throws UnsupportedEncodingException { + ToolOptions toolOptions = new ToolOptions(line, context); + Path inputFile = toolOptions.getPath(INPUT_FILE).orElseThrow(() -> new PowsyblException(INPUT_FILE + " option is missing")); + Path outputDir = toolOptions.getPath(OUTPUT_DIR).orElseThrow(() -> new PowsyblException(OUTPUT_DIR + " option is missing")); + Optional> ids = toolOptions.getValues(IDS); + context.getOutputStream().println("Loading network '" + inputFile + "'..."); + Network network = Importers.loadNetwork(inputFile); + if (network == null) { + throw new PowsyblException("File '" + inputFile + "' is not importable"); + } + ComponentLibrary componentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + LayoutParameters parameters = new LayoutParameters(); + VoltageLevelLayoutFactory voltageLevelLayoutFactory = new SmartVoltageLevelLayoutFactory(); + SubstationLayoutFactory substationLayoutFactory = new HorizontalSubstationLayoutFactory(); + if (ids.isPresent()) { + for (String id : ids.get()) { + VoltageLevel vl = network.getVoltageLevel(id); + if (vl == null) { + Substation s = network.getSubstation(id); + if (s == null) { + throw new PowsyblException("No voltage level or substation with id : '" + id + "'"); + } else { // id is a substation id + generateSvg(context, outputDir, componentLibrary, parameters, + voltageLevelLayoutFactory, substationLayoutFactory, s, network); + } + } else { // id is a voltage level id + generateSvg(context, outputDir, componentLibrary, parameters, voltageLevelLayoutFactory, vl, network); + } + } + } else { + boolean allVoltageLevels = toolOptions.hasOption(ALL_VOLTAGE_LEVELS); + boolean allSubstations = toolOptions.hasOption(ALL_SUBSTATIONS); + + // by default, export all voltage levels if no id given and no + // additional option (all-voltage-levels or all-substations) given + if (!allVoltageLevels && !allSubstations) { + allVoltageLevels = true; + } + + if (allVoltageLevels) { + // export all voltage levels + for (VoltageLevel vl : network.getVoltageLevels()) { + generateSvg(context, outputDir, componentLibrary, parameters, voltageLevelLayoutFactory, vl, network); + } + } + if (allSubstations) { + // export all substations + for (Substation s : network.getSubstations()) { + generateSvg(context, outputDir, componentLibrary, parameters, + voltageLevelLayoutFactory, substationLayoutFactory, s, network); + } + } + } + } +} diff --git a/single-line-diagram-view-app/pom.xml b/single-line-diagram-view-app/pom.xml new file mode 100644 index 000000000..e360c0a2c --- /dev/null +++ b/single-line-diagram-view-app/pom.xml @@ -0,0 +1,64 @@ + + + + 4.0.0 + + + powsybl-single-line-diagram + com.powsybl + 1.0.0-SNAPSHOT + + + powsybl-single-line-diagram-view-app + Single line diagram JavaFX view app + + + + com.powsybl + powsybl-single-line-diagram-view + ${project.version} + + + com.powsybl + powsybl-ucte-converter + ${powsyblcore.version} + + + + com.powsybl + powsybl-cgmes-conversion + runtime + + + com.powsybl + powsybl-config-classic + runtime + + + + com.powsybl + powsybl-triple-store-impl-rdf4j + runtime + + + org.eclipse.rdf4j + rdf4j-rio-rdfxml + runtime + + + org.eclipse.rdf4j + rdf4j-runtime + runtime + + + + \ No newline at end of file diff --git a/single-line-diagram-view-app/src/main/java/com/powsybl/sld/view/app/AbstractSubstationDiagramViewer.java b/single-line-diagram-view-app/src/main/java/com/powsybl/sld/view/app/AbstractSubstationDiagramViewer.java new file mode 100644 index 000000000..ac26d62ad --- /dev/null +++ b/single-line-diagram-view-app/src/main/java/com/powsybl/sld/view/app/AbstractSubstationDiagramViewer.java @@ -0,0 +1,989 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.view.app; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.ImmutableMap; +import com.powsybl.cgmes.iidm.extensions.dl.NetworkDiagramData; +import com.powsybl.commons.json.JsonUtil; +import com.powsybl.iidm.network.*; +import com.powsybl.sld.SubstationDiagram; +import com.powsybl.sld.VoltageLevelDiagram; +import com.powsybl.sld.cgmes.CgmesSubstationLayoutFactory; +import com.powsybl.sld.cgmes.CgmesVoltageLevelLayoutFactory; +import com.powsybl.sld.layout.*; +import com.powsybl.sld.library.ComponentLibrary; +import com.powsybl.sld.library.ResourcesComponentLibrary; +import com.powsybl.sld.svg.*; +import com.powsybl.sld.util.NominalVoltageSubstationDiagramStyleProvider; +import com.powsybl.sld.util.SmartVoltageLevelLayoutFactory; +import com.powsybl.sld.util.TopologicalStyleProvider; +import com.powsybl.sld.view.AbstractContainerDiagramView; +import com.powsybl.sld.view.DisplayVoltageLevel; +import com.powsybl.sld.view.SubstationDiagramView; +import com.powsybl.sld.view.VoltageLevelDiagramView; +import javafx.application.Application; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.beans.value.WeakChangeListener; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.concurrent.Service; +import javafx.concurrent.Task; +import javafx.geometry.Insets; +import javafx.geometry.Side; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.control.cell.CheckBoxTreeCell; +import javafx.scene.layout.*; +import javafx.scene.text.Font; +import javafx.scene.text.Text; +import javafx.stage.FileChooser; +import javafx.stage.Stage; +import javafx.util.StringConverter; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.ToDoubleFunction; +import java.util.prefs.Preferences; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public abstract class AbstractSubstationDiagramViewer extends Application implements DisplayVoltageLevel { + + protected static final Logger LOGGER = LoggerFactory.getLogger(AbstractSubstationDiagramViewer.class); + + private static final String SELECTED_VOLTAGE_LEVEL_IDS_PROPERTY = "selectedVoltageLevelIds"; + private static final String SELECTED_SUBSTATION_IDS_PROPERTY = "selectedSubstationIds"; + + private final Map voltageLevelsLayouts + = new ImmutableMap.Builder() + .put("Smart", new SmartVoltageLevelLayoutFactory()) + .put("Auto extensions", new PositionVoltageLevelLayoutFactory(new PositionFromExtension())) + .put("Auto without extensions", new PositionVoltageLevelLayoutFactory(new PositionFree())) + .put("Auto without extensions Clustering", new PositionVoltageLevelLayoutFactory(new PositionByClustering())) + .put("Random", new RandomVoltageLevelLayoutFactory(500, 500)) + .put("Cgmes", new CgmesVoltageLevelLayoutFactory()) + .build(); + + private final Map styles + = ImmutableMap.of("Default", new DefaultSubstationDiagramStyleProvider(), + "Nominal voltage", new NominalVoltageSubstationDiagramStyleProvider(), + "Topology", new TopologicalStyleProvider(null)); + + private final Map substationsLayouts + = ImmutableMap.of("Horizontal", new HorizontalSubstationLayoutFactory(), + "Vertical", new VerticalSubstationLayoutFactory(), + "Cgmes", new CgmesSubstationLayoutFactory()); + + private final ComponentLibrary convergenceComponentLibrary = new ResourcesComponentLibrary("/ConvergenceLibrary"); + + private final Map svgLibraries + = ImmutableMap.of("CVG Design", convergenceComponentLibrary); + + private final ObservableList selectableSubstations = FXCollections.observableArrayList(); + + private final ObservableList selectableVoltageLevels = FXCollections.observableArrayList(); + + private final TextField filterInput = new TextField(); + + private final TreeView substationsTree = new TreeView<>(); + + private final TabPane diagramsPane = new TabPane(); + private Tab tabSelected; + private Tab tabChecked; + private final BorderPane selectedDiagramPane = new BorderPane(); + private final TabPane checkedDiagramsPane = new TabPane(); + private GridPane parametersPane; + + private final ObjectProperty networkProperty = new SimpleObjectProperty<>(); + + private final ObjectProperty layoutParameters = new SimpleObjectProperty<>(new LayoutParameters() + .setShowGrid(true)); + + protected final Preferences preferences = Preferences.userNodeForPackage(VoltageLevelDiagramView.class); + + private final ObjectMapper objectMapper = JsonUtil.createObjectMapper(); + + protected final ComboBox voltageLevelLayoutComboBox = new ComboBox<>(); + + private final ComboBox substationLayoutComboBox = new ComboBox<>(); + + private final ComboBox styleComboBox = new ComboBox<>(); + + private final ComboBox svgLibraryComboBox = new ComboBox<>(); + + private final CheckBox showNames = new CheckBox("Show names"); + + private final CheckBox hideSubstations = new CheckBox("Hide substations"); + + private final ComboBox diagramNamesComboBox = new ComboBox<>(); + + private class ContainerDiagramPane extends BorderPane { + private final ScrollPane flowPane = new ScrollPane(); + + private final TextArea infoArea = new TextArea(); + + private final VBox svgArea = new VBox(); + private final TextField svgSearchField = new TextField(); + private final Button svgSearchButton = new Button("Search"); + private final TextArea svgTextArea = new TextArea(); + private AtomicReference svgSearchStart = new AtomicReference<>(0); + private final Button svgSaveButton = new Button("Save"); + + private final VBox metadataArea = new VBox(); + private final TextField metadataSearchField = new TextField(); + private final Button metadataSearchButton = new Button("Search"); + private final TextArea metadataTextArea = new TextArea(); + private final AtomicReference metadataSearchStart = new AtomicReference<>(0); + private final Button metadataSaveButton = new Button("Save"); + + private final Tab tab1 = new Tab("Diagram", flowPane); + + private final Tab tab2 = new Tab("SVG", svgArea); + + private final Tab tab3 = new Tab("Metadata", metadataArea); + + private final TabPane tabPane = new TabPane(tab1, tab2, tab3); + + private final TitledPane titledPane = new TitledPane("Infos", infoArea); + + private final ChangeListener listener; + + ContainerDiagramPane(Container c) { + createArea(svgSearchField, svgSearchButton, svgSaveButton, "SVG file", "*.svg", svgTextArea, svgArea, svgSearchStart); + createArea(metadataSearchField, metadataSearchButton, metadataSaveButton, "JSON file", "*.json", metadataTextArea, metadataArea, metadataSearchStart); + + infoArea.setEditable(false); + infoArea.setText(String.join(System.lineSeparator(), + "id: " + c.getId(), + "name: " + c.getName())); + tabPane.setSide(Side.BOTTOM); + tab1.setClosable(false); + tab2.setClosable(false); + tab3.setClosable(false); + setCenter(tabPane); + setBottom(titledPane); + listener = (observable, oldValue, newValue) -> loadDiagram(c); + layoutParameters.addListener(new WeakChangeListener<>(listener)); + loadDiagram(c); + } + + class ContainerDiagramResult { + + private final AbstractContainerDiagramView view; + + private final String svgData; + + private final String metadataData; + + ContainerDiagramResult(AbstractContainerDiagramView view, String svgData, String metadataData) { + this.view = view; + this.svgData = svgData; + this.metadataData = metadataData; + } + + AbstractContainerDiagramView getView() { + return view; + } + + String getSvgData() { + return svgData; + } + + String getMetadataData() { + return metadataData; + } + } + + private ScrollPane getFlowPane() { + return flowPane; + } + + private String getSelectedDiagramName() { + return diagramNamesComboBox.getSelectionModel().getSelectedItem(); + } + + private ContainerDiagramResult createContainerDiagramView(Container c) { + String svgData; + String metadataData; + try (StringWriter svgWriter = new StringWriter(); + StringWriter metadataWriter = new StringWriter()) { + SubstationDiagramStyleProvider styleProvider = styles.get(styleComboBox.getSelectionModel().getSelectedItem()); + SubstationDiagramInitialValueProvider initProvider = new DefaultSubstationDiagramInitialValueProvider(networkProperty.get()); + NodeLabelConfiguration nodeLabelConfiguration = new DefaultNodeLabelConfiguration(getComponentLibrary()); + + String dName = getSelectedDiagramName(); + LayoutParameters diagramLayoutParameters = new LayoutParameters(layoutParameters.get()).setDiagramName(dName); + if (c.getContainerType() == ContainerType.VOLTAGE_LEVEL) { + VoltageLevelDiagram diagram = VoltageLevelDiagram.build((VoltageLevel) c, getVoltageLevelLayoutFactory(), showNames.isSelected(), + layoutParameters.get().isShowInductorFor3WT()); + diagram.writeSvg("", getComponentLibrary(), + diagramLayoutParameters, + initProvider, + styleProvider, + nodeLabelConfiguration, + svgWriter, + metadataWriter); + } else if (c.getContainerType() == ContainerType.SUBSTATION) { + SubstationDiagram diagram = SubstationDiagram.build((Substation) c, getSubstationLayoutFactory(), getVoltageLevelLayoutFactory(), showNames.isSelected()); + diagram.writeSvg("", getComponentLibrary(), + diagramLayoutParameters, + initProvider, + styleProvider, + nodeLabelConfiguration, + svgWriter, + metadataWriter); + } + + svgWriter.flush(); + metadataWriter.flush(); + svgData = svgWriter.toString(); + metadataData = metadataWriter.toString(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + + AbstractContainerDiagramView diagramView = null; + try (InputStream svgInputStream = new ByteArrayInputStream(svgData.getBytes(StandardCharsets.UTF_8)); + InputStream metadataInputStream = new ByteArrayInputStream(metadataData.getBytes(StandardCharsets.UTF_8))) { + if (c.getContainerType() == ContainerType.VOLTAGE_LEVEL) { + diagramView = VoltageLevelDiagramView.load(svgInputStream, metadataInputStream, AbstractSubstationDiagramViewer.this); + } else if (c.getContainerType() == ContainerType.SUBSTATION) { + diagramView = SubstationDiagramView.load(svgInputStream, metadataInputStream, AbstractSubstationDiagramViewer.this); + } else { + throw new AssertionError(); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + return new ContainerDiagramResult(diagramView, svgData, metadataData); + } + + private void loadDiagram(Container c) { + Service loader = new Service() { + @Override + protected Task createTask() { + return new Task() { + @Override + protected ContainerDiagramResult call() { + return createContainerDiagramView(c); + } + }; + } + }; + loader.setOnScheduled(event -> { + Text loading = new Text("Loading..."); + loading.setFont(Font.font(30)); + flowPane.setContent(loading); + svgTextArea.setText(""); + metadataTextArea.setText(""); + }); + loader.setOnSucceeded(event -> { + ContainerDiagramResult result = (ContainerDiagramResult) event.getSource().getValue(); + if (result.getView() != null) { + flowPane.setContent(result.getView()); + } + svgTextArea.setText(result.getSvgData()); + metadataTextArea.setText(result.getMetadataData()); + }); + loader.setOnFailed(event -> { + Throwable e = event.getSource().getException(); + LOGGER.error(e.toString(), e); + }); + loader.start(); + } + + private ComponentLibrary getComponentLibrary() { + String selectedItem = svgLibraryComboBox.getSelectionModel().getSelectedItem(); + return svgLibraries.get(selectedItem); + } + + private SubstationLayoutFactory getSubstationLayoutFactory() { + String selectedItem = substationLayoutComboBox.getSelectionModel().getSelectedItem(); + return substationsLayouts.get(selectedItem); + } + + private void createArea(TextField searchField, Button searchButton, Button saveButton, + String descrSave, String extensionSave, + TextArea textArea, VBox area, + AtomicReference searchStart) { + HBox searchBox = new HBox(); + searchBox.setSpacing(20); + searchBox.setPadding(new Insets(10)); + searchField.setPrefColumnCount(35); + searchBox.getChildren().add(searchField); + searchBox.getChildren().add(searchButton); + searchBox.getChildren().add(saveButton); + + searchStart.set(0); + searchButton.setOnAction(evh -> { + String txtPattern = searchField.getText(); + Pattern pattern = Pattern.compile(txtPattern); + Matcher matcher = pattern.matcher(textArea.getText()); + boolean found = matcher.find(searchStart.get()); + if (found) { + textArea.selectRange(matcher.start(), matcher.end()); + searchStart.set(matcher.end()); + } else { + textArea.deselect(); + searchStart.set(0); + found = matcher.find(searchStart.get()); + if (found) { + textArea.selectRange(matcher.start(), matcher.end()); + searchStart.set(matcher.end()); + } + } + }); + searchField.textProperty().addListener((observable, oldValue, newValue) -> + searchStart.set(0) + ); + + saveButton.setOnAction(evh -> { + FileChooser fileChooser = new FileChooser(); + FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(descrSave, extensionSave); + fileChooser.getExtensionFilters().add(extFilter); + File file = fileChooser.showSaveDialog(getScene().getWindow()); + + if (file != null) { + try { + PrintWriter writer; + writer = new PrintWriter(file); + writer.println(textArea.getText()); + writer.close(); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + } + }); + + area.setSpacing(8); + area.getChildren().add(searchBox); + area.getChildren().add(textArea); + VBox.setVgrow(searchBox, Priority.NEVER); + VBox.setVgrow(textArea, Priority.ALWAYS); + textArea.setEditable(false); + } + } + + abstract class AbstractSelectableContainer { + + protected final String id; + + protected final String name; + + protected final BooleanProperty checkedProperty = new SimpleBooleanProperty(); + + AbstractSelectableContainer(String id, String name) { + this.id = id; + this.name = name; + checkedProperty.addListener((obs, wasSelected, isNowSelected) -> { + if (isNowSelected) { + addDiagramTab(); + } else { + removeDiagramTab(); + } + saveSelectedDiagrams(); + }); + } + + private void removeDiagramTab() { + checkedDiagramsPane.getTabs().removeIf(tab -> tab.getText().equals(id)); + } + + abstract void addDiagramTab(); + + protected String getId() { + return id; + } + + protected String getIdOrName() { + return showNames.isSelected() ? name : id; + } + + public BooleanProperty checkedProperty() { + return checkedProperty; + } + + public void setCheckedProperty(Boolean b) { + checkedProperty.setValue(b); + } + + @Override + public String toString() { + return getIdOrName(); + } + + private void saveSelectedDiagrams() { + try { + String selectedVoltageLevelIdsPropertyValue = objectMapper.writeValueAsString(selectableVoltageLevels.stream() + .filter(selectableVoltageLevel -> selectableVoltageLevel.checkedProperty().get()) + .map(SelectableVoltageLevel::getId) + .collect(Collectors.toList())); + preferences.put(SELECTED_VOLTAGE_LEVEL_IDS_PROPERTY, selectedVoltageLevelIdsPropertyValue); + + String selectedSubstationIdsPropertyValue = objectMapper.writeValueAsString(selectableSubstations.stream() + .filter(selectableSubstation -> selectableSubstation.checkedProperty().get()) + .map(SelectableSubstation::getId) + .collect(Collectors.toList())); + preferences.put(SELECTED_SUBSTATION_IDS_PROPERTY, selectedSubstationIdsPropertyValue); + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + + private class SelectableVoltageLevel extends AbstractSelectableContainer { + + SelectableVoltageLevel(String id, String name) { + super(id, name); + } + + @Override + protected void addDiagramTab() { + VoltageLevel vl = networkProperty.get().getVoltageLevel(id); + if (vl != null) { + Tab tab = new Tab(id, new ContainerDiagramPane(vl)); + tab.setTooltip(new Tooltip(vl.getName())); + tab.setOnCloseRequest(event -> { + checkedProperty.set(false); + checkvItemTree(id, false); + }); + checkedDiagramsPane.getTabs().add(tab); + checkedDiagramsPane.getSelectionModel().select(tab); + } else { + LOGGER.warn("Voltage level {} not found", id); + } + } + } + + private class SelectableSubstation extends AbstractSelectableContainer { + SelectableSubstation(String id, String name) { + super(id, name); + } + + @Override + protected void addDiagramTab() { + Substation s = networkProperty.get().getSubstation(id); + if (s != null) { + Tab tab = new Tab(id, new ContainerDiagramPane(s)); + tab.setTooltip(new Tooltip(s.getName())); + tab.setOnCloseRequest(event -> { + checkedProperty.set(false); + checksItemTree(id, false); + }); + checkedDiagramsPane.getTabs().add(tab); + checkedDiagramsPane.getSelectionModel().select(tab); + } else { + LOGGER.warn("Substation {} not found", id); + } + } + + private void checksItemTree(String id, boolean selected) { + substationsTree.getRoot().getChildren().stream().forEach(child -> { + if (child.getValue().getId().equals(id)) { + ((CheckBoxTreeItem) child).setSelected(selected); + } + }); + } + } + + private VoltageLevelLayoutFactory getVoltageLevelLayoutFactory() { + String selectedItem = voltageLevelLayoutComboBox.getSelectionModel().getSelectedItem(); + return voltageLevelsLayouts.get(selectedItem); + } + + private void setParameters(LayoutParameters layoutParameters) { + this.layoutParameters.set(new LayoutParameters(layoutParameters)); + } + + private void addSpinner(String label, double min, double max, double amountToStepBy, int row, + ToDoubleFunction initializer, + BiFunction updater) { + Spinner spinner = new Spinner<>(min, max, initializer.applyAsDouble(layoutParameters.get()), amountToStepBy); + spinner.setEditable(true); + spinner.valueProperty().addListener((observable, oldValue, newValue) -> setParameters(updater.apply(layoutParameters.get(), newValue))); + parametersPane.add(new Label(label), 0, row); + parametersPane.add(spinner, 0, row + 1); + } + + private void addCheckBox(String label, int row, + Predicate initializer, + BiFunction updater) { + CheckBox cb = new CheckBox(label); + cb.setSelected(initializer.test(layoutParameters.get())); + cb.selectedProperty().addListener((observable, oldValue, newValue) -> setParameters(updater.apply(layoutParameters.get(), newValue))); + parametersPane.add(cb, 0, row); + } + + private void initPositionLayoutCheckBox(Predicate initializer, CheckBox stackCb) { + VoltageLevelLayoutFactory layoutFactory = getVoltageLevelLayoutFactory(); + stackCb.setSelected(layoutFactory instanceof PositionVoltageLevelLayoutFactory && initializer.test((PositionVoltageLevelLayoutFactory) layoutFactory)); + stackCb.setDisable(!(layoutFactory instanceof PositionVoltageLevelLayoutFactory)); + } + + private void addPositionLayoutCheckBox(String label, int rowIndex, Predicate initializer, + BiFunction updater) { + CheckBox stackCb = new CheckBox(label); + initPositionLayoutCheckBox(initializer, stackCb); + voltageLevelLayoutComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> initPositionLayoutCheckBox(initializer, stackCb)); + stackCb.selectedProperty().addListener((observable, oldValue, newValue) -> { + VoltageLevelLayoutFactory layoutFactory = getVoltageLevelLayoutFactory(); + if (layoutFactory instanceof PositionVoltageLevelLayoutFactory) { + updater.apply((PositionVoltageLevelLayoutFactory) layoutFactory, newValue); + // just to trigger diagram update + refreshDiagram(); + } + }); + + parametersPane.add(stackCb, 0, rowIndex); + } + + private void createParametersPane() { + parametersPane = new GridPane(); + parametersPane.setHgap(5); + parametersPane.setVgap(5); + parametersPane.setPadding(new Insets(5, 5, 5, 5)); + + int rowIndex = 0; + + Button fitToContent = new Button("Fit to content"); + fitToContent.setOnAction(event -> { + ContainerDiagramPane pane = null; + Tab tab = diagramsPane.getSelectionModel().getSelectedItem(); + if (tab != null) { + if (tab == tabChecked) { + if (checkedDiagramsPane.getSelectionModel().getSelectedItem() != null) { + pane = (ContainerDiagramPane) checkedDiagramsPane.getSelectionModel().getSelectedItem().getContent(); + } + } else { + pane = (ContainerDiagramPane) selectedDiagramPane.getCenter(); + } + if (pane != null) { + ((AbstractContainerDiagramView) pane.getFlowPane().getContent()).fitToContent( + pane.getFlowPane().getViewportBounds().getWidth(), 20., + pane.getFlowPane().getViewportBounds().getHeight(), 20.); + pane.getFlowPane().setHvalue(pane.getFlowPane().getHmin()); + pane.getFlowPane().setVvalue(pane.getFlowPane().getVmin()); + } + } + }); + + parametersPane.add(fitToContent, 0, rowIndex++); + + // svg library list + svgLibraryComboBox.getItems().addAll(svgLibraries.keySet()); + svgLibraryComboBox.getSelectionModel().selectFirst(); + svgLibraryComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> refreshDiagram()); + parametersPane.add(new Label("Design:"), 0, rowIndex++); + parametersPane.add(svgLibraryComboBox, 0, rowIndex++); + + styleComboBox.getItems().addAll(styles.keySet()); + styleComboBox.getSelectionModel().selectFirst(); + styleComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> refreshDiagram()); + parametersPane.add(new Label("Style:"), 0, rowIndex++); + parametersPane.add(styleComboBox, 0, rowIndex++); + + // substation layout list + substationLayoutComboBox.getItems().addAll(substationsLayouts.keySet()); + substationLayoutComboBox.getSelectionModel().selectFirst(); + substationLayoutComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> refreshDiagram()); + parametersPane.add(new Label("Substation Layout:"), 0, rowIndex++); + parametersPane.add(substationLayoutComboBox, 0, rowIndex++); + + // voltageLevel layout list + voltageLevelLayoutComboBox.getItems().addAll(voltageLevelsLayouts.keySet()); + voltageLevelLayoutComboBox.getSelectionModel().selectFirst(); + voltageLevelLayoutComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> refreshDiagram()); + + parametersPane.add(new Label("VoltageLevel Layout:"), 0, rowIndex++); + parametersPane.add(voltageLevelLayoutComboBox, 0, rowIndex++); + + //CGMES-DL diagrams names list + parametersPane.add(new Label("CGMES-DL Diagrams:"), 0, ++rowIndex); + parametersPane.add(diagramNamesComboBox, 0, ++rowIndex); + diagramNamesComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> refreshDiagram()); + diagramNamesComboBox.setDisable(true); + voltageLevelLayoutComboBox.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> setDiagramsNamesContent(networkProperty.get(), false)); + rowIndex += 1; + + addSpinner("Horizontal substation padding:", 50, 300, 5, rowIndex, LayoutParameters::getHorizontalSubstationPadding, LayoutParameters::setHorizontalSubstationPadding); + rowIndex += 2; + addSpinner("Vertical substation padding:", 50, 300, 5, rowIndex, LayoutParameters::getVerticalSubstationPadding, LayoutParameters::setVerticalSubstationPadding); + rowIndex += 2; + addSpinner("Initial busbar X:", 0, 100, 5, rowIndex, LayoutParameters::getInitialXBus, LayoutParameters::setInitialXBus); + rowIndex += 2; + addSpinner("Initial busbar Y:", 0, 500, 5, rowIndex, LayoutParameters::getInitialYBus, LayoutParameters::setInitialYBus); + rowIndex += 2; + addSpinner("Busbar vertical space:", 10, 100, 5, rowIndex, LayoutParameters::getVerticalSpaceBus, LayoutParameters::setVerticalSpaceBus); + rowIndex += 2; + addSpinner("Horizontal busbar padding:", 10, 100, 5, rowIndex, LayoutParameters::getHorizontalBusPadding, LayoutParameters::setHorizontalBusPadding); + rowIndex += 2; + addSpinner("Cell width:", 10, 100, 5, rowIndex, LayoutParameters::getCellWidth, LayoutParameters::setCellWidth); + rowIndex += 2; + addSpinner("Extern cell height:", 100, 500, 10, rowIndex, LayoutParameters::getExternCellHeight, LayoutParameters::setExternCellHeight); + rowIndex += 2; + addSpinner("Intern cell height:", 10, 100, 5, rowIndex, LayoutParameters::getInternCellHeight, LayoutParameters::setInternCellHeight); + rowIndex += 2; + addSpinner("Stack height:", 10, 100, 5, rowIndex, LayoutParameters::getStackHeight, LayoutParameters::setStackHeight); + rowIndex += 2; + addCheckBox("Show grid", rowIndex, LayoutParameters::isShowGrid, LayoutParameters::setShowGrid); + rowIndex += 1; + addCheckBox("Show internal nodes", rowIndex, LayoutParameters::isShowInternalNodes, LayoutParameters::setShowInternalNodes); + rowIndex += 1; + addCheckBox("Draw straight wires", rowIndex, LayoutParameters::isDrawStraightWires, LayoutParameters::setDrawStraightWires); + rowIndex += 1; + addPositionLayoutCheckBox("Stack feeders", rowIndex, PositionVoltageLevelLayoutFactory::isFeederStacked, PositionVoltageLevelLayoutFactory::setFeederStacked); + rowIndex += 1; + addPositionLayoutCheckBox("Remove fictitious nodes", rowIndex, PositionVoltageLevelLayoutFactory::isRemoveUnnecessaryFictitiousNodes, PositionVoltageLevelLayoutFactory::setRemoveUnnecessaryFictitiousNodes); + rowIndex += 1; + addPositionLayoutCheckBox("Substitute singular fictitious nodes", rowIndex, PositionVoltageLevelLayoutFactory::isSubstituteSingularFictitiousByFeederNode, PositionVoltageLevelLayoutFactory::setSubstituteSingularFictitiousByFeederNode); + rowIndex += 1; + addCheckBox("Show inductor for three windings transformers", rowIndex, LayoutParameters::isShowInductorFor3WT, LayoutParameters::setShowInductorFor3WT); + rowIndex += 1; + addCheckBox("Shift feeders height", rowIndex, LayoutParameters::isShiftFeedersPosition, LayoutParameters::setShiftFeedersPosition); + rowIndex += 1; + addSpinner("Shift feeders height scale factor:", 1, 10, 1, rowIndex, LayoutParameters::getScaleShiftFeedersPosition, LayoutParameters::setScaleShiftFeedersPosition); + rowIndex += 2; + addSpinner("Scale factor:", 1, 20, 1, rowIndex, LayoutParameters::getScaleFactor, LayoutParameters::setScaleFactor); + rowIndex += 2; + addSpinner("Arrows distance:", 10, 800, 5, rowIndex, LayoutParameters::getArrowDistance, LayoutParameters::setArrowDistance); + rowIndex += 2; + addCheckBox("Avoid SVG components duplication", rowIndex, LayoutParameters::isAvoidSVGComponentsDuplication, LayoutParameters::setAvoidSVGComponentsDuplication); + } + + private void setDiagramsNamesContent(Network network, boolean setValues) { + if (network != null && NetworkDiagramData.checkNetworkDiagramData(network)) { + if (setValues) { + diagramNamesComboBox.getItems().setAll(NetworkDiagramData.getDiagramsNames(network)); + diagramNamesComboBox.getSelectionModel().clearSelection(); + diagramNamesComboBox.setValue(null); + } + diagramNamesComboBox.setDisable(!(getVoltageLevelLayoutFactory() instanceof CgmesVoltageLevelLayoutFactory)); + } else { + diagramNamesComboBox.getItems().clear(); + diagramNamesComboBox.setDisable(true); + } + } + + private void refreshDiagram() { + layoutParameters.set(new LayoutParameters(layoutParameters.get())); + } + + private void loadSelectedVoltageLevelsDiagrams() { + String selectedIdsPropertyValue = preferences.get(SELECTED_VOLTAGE_LEVEL_IDS_PROPERTY, null); + if (selectedIdsPropertyValue != null) { + try { + Set selectedIds = new HashSet<>(objectMapper.readValue(selectedIdsPropertyValue, new TypeReference>() { + })); + selectableVoltageLevels.stream() + .filter(selectableObject -> selectedIds.contains(selectableObject.getId())) + .forEach(selectableVoltageLevel -> selectableVoltageLevel.checkedProperty().set(true)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + + private void loadSelectedSubstationsDiagrams() { + String selectedPropertyValue = preferences.get(SELECTED_SUBSTATION_IDS_PROPERTY, null); + + if (selectedPropertyValue != null) { + try { + Set selectedSubstationIds = new HashSet<>(objectMapper.readValue(selectedPropertyValue, new TypeReference>() { + })); + selectableSubstations.stream() + .filter(selectableSubstation -> selectedSubstationIds.contains(selectableSubstation.getId())) + .forEach(selectableSubstation -> selectableSubstation.checkedProperty().set(true)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + } + + /* + Handling the display of names/id in the substations tree + */ + private void initTreeCellFactory() { + substationsTree.setCellFactory(param -> { + CheckBoxTreeCell treeCell = new CheckBoxTreeCell<>(); + StringConverter> strConvert = new StringConverter>() { + @Override + public String toString(TreeItem c) { + if (c.getValue() != null) { + return showNames.isSelected() ? c.getValue().getName() : c.getValue().getId(); + } else { + return ""; + } + } + + @Override + public TreeItem fromString(String string) { + return null; + } + }; + treeCell.setConverter(strConvert); + return treeCell; + }); + } + + @Override + public void start(Stage primaryStage) { + initTreeCellFactory(); + + showNames.selectedProperty().addListener((observable, oldValue, newValue) -> { + substationsTree.refresh(); + refreshDiagram(); + }); + + hideSubstations.selectedProperty().addListener((observable, oldValue, newValue) -> { + initSubstationsTree(); + substationsTree.refresh(); + }); + + filterInput.textProperty().addListener(obs -> + initSubstationsTree() + ); + + // handling the change of the network + networkProperty.addListener((observable, oldNetwork, newNetwork) -> { + if (newNetwork == null) { + selectableVoltageLevels.clear(); + selectableSubstations.clear(); + } else { + selectableVoltageLevels.setAll(newNetwork.getVoltageLevelStream() + .map(vl -> new SelectableVoltageLevel(vl.getId(), vl.getName())) + .collect(Collectors.toList())); + selectableSubstations.setAll(newNetwork.getSubstationStream() + .map(s -> new SelectableSubstation(s.getId(), s.getName())) + .collect(Collectors.toList())); + } + }); + diagramsPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); + tabSelected = new Tab("Selected", selectedDiagramPane); + tabChecked = new Tab("Checked", checkedDiagramsPane); + diagramsPane.getTabs().setAll(tabSelected, tabChecked); + + createParametersPane(); + + BorderPane voltageLevelPane = new BorderPane(); + Label filterLabel = new Label("Filter:"); + filterLabel.setMinWidth(40); + GridPane voltageLevelToolBar = new GridPane(); + voltageLevelToolBar.setHgap(5); + voltageLevelToolBar.setVgap(5); + voltageLevelToolBar.setPadding(new Insets(5, 5, 5, 5)); + voltageLevelToolBar.add(showNames, 0, 0, 2, 1); + voltageLevelToolBar.add(hideSubstations, 0, 1, 2, 1); + voltageLevelToolBar.add(filterLabel, 0, 2); + voltageLevelToolBar.add(filterInput, 1, 2); + ColumnConstraints c0 = new ColumnConstraints(); + ColumnConstraints c1 = new ColumnConstraints(); + c1.setHgrow(Priority.ALWAYS); + voltageLevelToolBar.getColumnConstraints().addAll(c0, c1); + voltageLevelPane.setTop(voltageLevelToolBar); + voltageLevelPane.setCenter(substationsTree); + + SplitPane splitPane = new SplitPane(voltageLevelPane, diagramsPane, new ScrollPane(parametersPane)); + splitPane.setDividerPositions(0.2, 0.7, 0.1); + + Node casePane = createCasePane(primaryStage); + BorderPane.setMargin(casePane, new Insets(3, 3, 3, 3)); + BorderPane mainPane = new BorderPane(); + mainPane.setCenter(splitPane); + mainPane.setTop(casePane); + + // selected voltegeLevels diagrams reloading + selectableVoltageLevels.addListener(new ListChangeListener() { + @Override + public void onChanged(Change c) { + loadSelectedVoltageLevelsDiagrams(); + selectableVoltageLevels.remove(this); + } + }); + + // selected substation diagrams reloading + selectableSubstations.addListener(new ListChangeListener() { + @Override + public void onChanged(Change c) { + loadSelectedSubstationsDiagrams(); + selectableSubstations.remove(this); + } + }); + + // Handling selection of a substation or a voltageLevel in the substations tree + substationsTree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener>() { + @Override + public void changed(ObservableValue> observable, TreeItem oldValue, TreeItem newValue) { + if (newValue == null) { + return; + } + Container c = newValue.getValue(); + selectedDiagramPane.setCenter(new ContainerDiagramPane(c)); + } + }); + + // case reloading + loadNetworkFromPreferences(); + + Scene scene = new Scene(mainPane, 1000, 800); + primaryStage.setTitle("Substation diagram viewer"); + primaryStage.setScene(scene); + primaryStage.show(); + } + + protected void loadNetworkFromPreferences() { + } + + protected abstract Node createCasePane(Stage primaryStage); + + /* + check/uncheck a voltageLevel in the substations tree + */ + private void checkVoltageLevel(VoltageLevel v, Boolean checked) { + selectableVoltageLevels.stream() + .filter(selectableVoltageLevel -> selectableVoltageLevel.getIdOrName().equals(showNames.isSelected() ? v.getName() : v.getId())) + .forEach(selectableVoltageLevel -> selectableVoltageLevel.setCheckedProperty(checked)); + } + + /* + check/uncheck a substation in the substations tree + */ + private void checkSubstation(Substation s, Boolean checked) { + selectableSubstations.stream() + .filter(selectableSubstation -> selectableSubstation.getIdOrName().equals(showNames.isSelected() ? s.getName() : s.getId())) + .forEach(selectableSubstation -> selectableSubstation.setCheckedProperty(checked)); + } + + private void initVoltageLevelsTree(TreeItem rootItem, + Substation s, String filter, boolean emptyFilter, + Map mapSubstations, + Map mapVoltageLevels) { + boolean firstVL = true; + CheckBoxTreeItem sItem = null; + + for (VoltageLevel v : s.getVoltageLevels()) { + boolean vlOk = showNames.isSelected() ? v.getName().contains(filter) : v.getId().contains(filter); + + if (!emptyFilter && !vlOk) { + continue; + } + + CheckBoxTreeItem vItem = new CheckBoxTreeItem<>(v); + vItem.setIndependent(true); + if (mapVoltageLevels.containsKey(v.getId()) && mapVoltageLevels.get(v.getId()).checkedProperty().get()) { + vItem.setSelected(true); + } + + if (firstVL && !hideSubstations.isSelected()) { + sItem = new CheckBoxTreeItem<>(s); + sItem.setIndependent(true); + sItem.setExpanded(true); + if (mapSubstations.containsKey(s.getId()) && mapSubstations.get(s.getId()).checkedProperty().get()) { + sItem.setSelected(true); + } + rootItem.getChildren().add(sItem); + sItem.selectedProperty().addListener((obs, oldVal, newVal) -> + checkSubstation(s, newVal) + ); + } + + firstVL = false; + + if (sItem != null) { + sItem.getChildren().add(vItem); + } else { + rootItem.getChildren().add(vItem); + } + + vItem.selectedProperty().addListener((obs, oldVal, newVal) -> + checkVoltageLevel(v, newVal)); + } + } + + private void initSubstationsTree() { + String filter = filterInput.getText(); + boolean emptyFilter = StringUtils.isEmpty(filter); + + Network n = networkProperty.get(); + TreeItem rootItem = new TreeItem<>(); + rootItem.setExpanded(true); + + Map mapSubstations = selectableSubstations.stream() + .collect(Collectors.toMap(SelectableSubstation::getId, Function.identity())); + Map mapVoltageLevels = selectableVoltageLevels.stream() + .collect(Collectors.toMap(SelectableVoltageLevel::getId, Function.identity())); + + for (Substation s : n.getSubstations()) { + initVoltageLevelsTree(rootItem, s, filter, emptyFilter, mapSubstations, mapVoltageLevels); + } + + if (substationsTree.getRoot() != null) { + substationsTree.getRoot().getChildren().clear(); + } + + substationsTree.setRoot(rootItem); + substationsTree.setShowRoot(false); + } + + @Override + public void display(String voltageLevelId) { + VoltageLevel v = networkProperty.get().getVoltageLevel(voltageLevelId); + if (diagramsPane.getSelectionModel().getSelectedItem() == tabChecked) { + checkVoltageLevel(v, true); + checkvItemTree(voltageLevelId, true); + checkedDiagramsPane.getTabs().stream().forEach(tab -> { + if (tab.getText().equals(voltageLevelId)) { + checkedDiagramsPane.getSelectionModel().select(tab); + } + }); + } else if (diagramsPane.getSelectionModel().getSelectedItem() == tabSelected) { + selectedDiagramPane.setCenter(new ContainerDiagramPane(v)); + } + } + + private void checkvItemTree(String id, boolean selected) { + substationsTree.getRoot().getChildren().stream().forEach(childS -> + childS.getChildren().stream().forEach(childV -> { + if (childV.getValue().getId().equals(id)) { + ((CheckBoxTreeItem) childV).setSelected(selected); + } + }) + ); + } + + protected void setNetwork(Network network) { + networkProperty.setValue(network); + initSubstationsTree(); + setDiagramsNamesContent(network, true); + } +} diff --git a/single-line-diagram-view-app/src/main/java/com/powsybl/sld/view/app/SubstationDiagramViewer.java b/single-line-diagram-view-app/src/main/java/com/powsybl/sld/view/app/SubstationDiagramViewer.java new file mode 100644 index 000000000..38588d666 --- /dev/null +++ b/single-line-diagram-view-app/src/main/java/com/powsybl/sld/view/app/SubstationDiagramViewer.java @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.view.app; + +import com.powsybl.computation.local.LocalComputationManager; +import com.powsybl.iidm.import_.ImportConfig; +import com.powsybl.iidm.import_.Importers; +import com.powsybl.iidm.network.Network; +import javafx.concurrent.Service; +import javafx.concurrent.Task; +import javafx.geometry.Insets; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.TextField; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.stage.FileChooser; +import javafx.stage.Stage; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +/** + * @author Geoffroy Jamgotchian + */ +public class SubstationDiagramViewer extends AbstractSubstationDiagramViewer { + + private static final String CASE_PATH_PROPERTY = "casePath"; + + private final Button caseLoadingStatus = new Button(" "); + private final TextField casePathTextField = new TextField(); + + @Override + protected Node createCasePane(Stage primaryStage) { + caseLoadingStatus.setStyle("-fx-background-color: red"); + casePathTextField.setEditable(false); + + HBox.setHgrow(casePathTextField, Priority.ALWAYS); + + Button caseButton = new Button("..."); + caseButton.setOnAction(event -> { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Open case File"); + File file = fileChooser.showOpenDialog(primaryStage); + if (file != null) { + loadNetwork(file.toPath()); + } + }); + HBox casePane = new HBox(3, caseLoadingStatus, casePathTextField, caseButton); + BorderPane.setMargin(casePane, new Insets(3, 3, 3, 3)); + return casePane; + } + + @Override + protected void loadNetworkFromPreferences() { + String casePathPropertyValue = preferences.get(CASE_PATH_PROPERTY, null); + if (casePathPropertyValue != null) { + loadNetwork(Paths.get(casePathPropertyValue)); + } + } + + private void loadNetwork(Path file) { + Service networkService = new Service() { + @Override + protected Task createTask() { + return new Task() { + @Override + protected Network call() { + Properties properties = new Properties(); + properties.put("iidm.import.cgmes.post-processors", "cgmesDLImport"); + return Importers.loadNetwork(file, LocalComputationManager.getDefault(), new ImportConfig(), properties); + } + }; + } + }; + networkService.setOnRunning(event -> { + caseLoadingStatus.setStyle("-fx-background-color: yellow"); + casePathTextField.setText(file.toAbsolutePath().toString()); + }); + networkService.setOnSucceeded(event -> { + setNetwork((Network) event.getSource().getValue()); + caseLoadingStatus.setStyle("-fx-background-color: green"); + preferences.put(CASE_PATH_PROPERTY, file.toAbsolutePath().toString()); + }); + networkService.setOnFailed(event -> { + Throwable exception = event.getSource().getException(); + LOGGER.error(exception.toString(), exception); + casePathTextField.setText(""); + caseLoadingStatus.setStyle("-fx-background-color: red"); + }); + networkService.start(); + } + + public static void main(String[] args) { + launch(args); + } +} diff --git a/single-line-diagram-view/pom.xml b/single-line-diagram-view/pom.xml new file mode 100644 index 000000000..fbc289d29 --- /dev/null +++ b/single-line-diagram-view/pom.xml @@ -0,0 +1,49 @@ + + + + 4.0.0 + + + powsybl-single-line-diagram + com.powsybl + 1.0.0-SNAPSHOT + + + powsybl-single-line-diagram-view + Single line diagram JavaFX view component + + + + com.github.afester.javafx + FranzXaver + + + + com.powsybl + powsybl-single-line-diagram-core + ${project.version} + + + com.powsybl + powsybl-single-line-diagram-util + ${project.version} + + + + junit + junit + test + + + + + \ No newline at end of file diff --git a/single-line-diagram-view/src/main/java/com/powsybl/sld/view/AbstractContainerDiagramView.java b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/AbstractContainerDiagramView.java new file mode 100644 index 000000000..a45ab5112 --- /dev/null +++ b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/AbstractContainerDiagramView.java @@ -0,0 +1,216 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.view; + +import afester.javafx.svg.SvgLoader; +import com.powsybl.commons.PowsyblException; +import com.powsybl.sld.svg.GraphMetadata; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.input.MouseButton; +import javafx.scene.layout.BorderPane; +import javafx.scene.shape.Polyline; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.powsybl.sld.library.ComponentTypeName.BREAKER; +import static com.powsybl.sld.library.ComponentTypeName.DISCONNECTOR; +import static com.powsybl.sld.library.ComponentTypeName.LOAD_BREAK_SWITCH; + +/** + * @author Franck Lecuyer + */ +public abstract class AbstractContainerDiagramView extends BorderPane { + private static final Logger LOGGER = LoggerFactory.getLogger(AbstractContainerDiagramView.class); + + private double pressedX; + private double pressedY; + private final Group svgImage; + + protected AbstractContainerDiagramView(Group svgImage) { + super(svgImage); + this.svgImage = svgImage; + + registerEvents(); + } + + /* + * Resizing the group of nodes to fit the viewport scrollpane dimensions + */ + public void fitToContent(double viewportWidth, double dWidth, + double viewportHeight, double dHeight) { + double boundsWidth = svgImage.getBoundsInParent().getWidth(); + double boundsHeight = svgImage.getBoundsInParent().getHeight(); + + double scaleX = 1.; + double scaleY = 1.; + if (boundsWidth > boundsHeight) { + scaleX = (viewportWidth - dWidth) / boundsWidth; + scaleY = scaleX; + } else { + scaleY = (viewportHeight - dHeight) / boundsHeight; + scaleX = scaleY; + } + + svgImage.setScaleX(svgImage.getScaleX() * scaleX); + svgImage.setScaleY(svgImage.getScaleY() * scaleY); + svgImage.setTranslateX(svgImage.getTranslateX() - svgImage.getBoundsInParent().getMinX() + dWidth / 2); + svgImage.setTranslateY(svgImage.getTranslateY() - svgImage.getBoundsInParent().getMinY() + dHeight / 2); + } + + private void registerEvents() { + setOnScroll(event -> { + double zoomFactor = 1.05; + double deltaY = event.getDeltaY(); + if (deltaY < 0) { + zoomFactor = 2.0 - zoomFactor; + } + svgImage.setScaleX(svgImage.getScaleX() * zoomFactor); + svgImage.setScaleY(svgImage.getScaleY() * zoomFactor); + + event.consume(); + }); + + setOnMousePressed(event -> { + if (event.getButton().equals(MouseButton.SECONDARY)) { + pressedX = -svgImage.getTranslateX() + event.getX(); + pressedY = -svgImage.getTranslateY() + event.getY(); + } + event.consume(); + }); + setOnMouseDragged(event -> { + if (event.getButton().equals(MouseButton.SECONDARY)) { + svgImage.setTranslateX(event.getX() - pressedX); + svgImage.setTranslateY(event.getY() - pressedY); + } + event.consume(); + }); + } + + private static void installHandlers(Node node, GraphMetadata metadata, + Map wireHandlers, + Map nodeHandlers, + Map vlHandlers, + DisplayVoltageLevel displayVL) { + if (node == null) { + return; + } + + if (!StringUtils.isEmpty(node.getId())) { + GraphMetadata.NodeMetadata nodeMetadata = metadata.getNodeMetadata(node.getId()); + if (nodeMetadata != null) { + if (node instanceof Group && + (nodeMetadata.getComponentType() != null) && + (nodeMetadata.getComponentType().equals(BREAKER) || nodeMetadata.getComponentType().equals(DISCONNECTOR) || nodeMetadata.getComponentType().equals(LOAD_BREAK_SWITCH))) { + setNodeVisibility((Group) node, nodeMetadata); + } + installNodeHandlers(node, metadata, nodeMetadata, nodeHandlers, vlHandlers, displayVL); + } + GraphMetadata.WireMetadata wireMetadata = metadata.getWireMetadata(node.getId()); + if (wireMetadata != null) { + installWireHandlers(node, metadata, wireMetadata, nodeHandlers, wireHandlers); + } + GraphMetadata.ArrowMetadata arrowMetadata = metadata.getArrowMetadata(node.getId()); + if (arrowMetadata != null) { + WireHandler wireHandler = wireHandlers.get(arrowMetadata.getWireId()); + wireHandler.addArrow((Group) node); + } + } + + // propagate to children + if (node instanceof Group) { + ((Group) node).getChildren().forEach(child -> installHandlers(child, metadata, wireHandlers, nodeHandlers, vlHandlers, displayVL)); + } + } + + private static void installNodeHandlers(Node node, GraphMetadata metadata, + GraphMetadata.NodeMetadata nodeMetadata, + Map nodeHandlers, + Map vlHandlers, + DisplayVoltageLevel displayVL) { + if (!nodeMetadata.isVLabel()) { + NodeHandler nodeHandler = new NodeHandler(node, nodeMetadata.getComponentType(), + nodeMetadata.getRotationAngle(), + metadata, + nodeMetadata.getVId(), nodeMetadata.getNextVId(), + nodeMetadata.getDirection()); + nodeHandler.setDisplayVL(displayVL); + LOGGER.trace("Add handler to node {} in voltageLevel {}", node.getId(), nodeMetadata.getVId()); + nodeHandlers.put(node.getId(), nodeHandler); + } else { // handler for voltageLevel label + VoltageLevelHandler vlHandler = new VoltageLevelHandler(node, metadata, nodeMetadata.getVId()); + LOGGER.trace("Add handler to voltageLvel label {}", node.getId()); + vlHandlers.put(nodeMetadata.getVId(), vlHandler); + } + } + + private static void installWireHandlers(Node node, GraphMetadata metadata, GraphMetadata.WireMetadata wireMetadata, Map nodeHandlers, Map wireHandlers) { + NodeHandler nodeHandler1 = nodeHandlers.get(wireMetadata.getNodeId1()); + if (nodeHandler1 == null) { + throw new PowsyblException("Node 1 " + wireMetadata.getNodeId1() + " not found"); + } + NodeHandler nodeHandler2 = nodeHandlers.get(wireMetadata.getNodeId2()); + if (nodeHandler2 == null) { + throw new PowsyblException("Node 2 " + wireMetadata.getNodeId2() + " not found"); + } + WireHandler wireHandler = new WireHandler((Polyline) node, nodeHandler1, nodeHandler2, wireMetadata.isStraight(), + wireMetadata.isSnakeLine(), metadata); + LOGGER.trace(" Added handler to wire between {} and {}", wireMetadata.getNodeId1(), wireMetadata.getNodeId2()); + wireHandlers.put(node.getId(), wireHandler); + } + + private static void setNodeVisibility(Group node, GraphMetadata.NodeMetadata nodeMetadata) { + node.getChildren().forEach(child -> + child.setVisible((nodeMetadata.isOpen() && child.getId().endsWith("open")) + || (!nodeMetadata.isOpen() && child.getId().endsWith("closed")))); + } + + private static void installHandlers(Node node, GraphMetadata metadata, + DisplayVoltageLevel displayVL) { + Map wireHandlers = new HashMap<>(); + Map nodeHandlers = new HashMap<>(); + Map vlHandlers = new HashMap<>(); + + installHandlers(node, metadata, wireHandlers, nodeHandlers, vlHandlers, displayVL); + + // resolve links + for (WireHandler wireHandler : wireHandlers.values()) { + wireHandler.getNodeHandler1().addWire(wireHandler); + wireHandler.getNodeHandler2().addWire(wireHandler); + } + + // resolve voltageLevel handler + vlHandlers.values().forEach(v -> v.addNodeHandlers(nodeHandlers.values().stream().collect(Collectors.toList()))); + } + + protected static Group loadSvgAndMetadata(InputStream svgInputStream, + InputStream metadataInputStream, + DisplayVoltageLevel displayVL) { + // convert svg file to JavaFX components + Group svgImage = null; + try { + svgImage = new SvgLoader().loadSvg(svgInputStream); + } catch (Exception e) { + // to feed the content of the 'SVG' and 'Metadata' tab, even if the + // svg diagram cannot be loaded by svg loader + } + + // load metadata + GraphMetadata metadata = GraphMetadata.parseJson(metadataInputStream); + + // install node and wire handlers to allow diagram edition + installHandlers(svgImage, metadata, displayVL); + + return svgImage; + } +} diff --git a/single-line-diagram-view/src/main/java/com/powsybl/sld/view/DisplayVoltageLevel.java b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/DisplayVoltageLevel.java new file mode 100644 index 000000000..6fa59fe12 --- /dev/null +++ b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/DisplayVoltageLevel.java @@ -0,0 +1,5 @@ +package com.powsybl.sld.view; + +public interface DisplayVoltageLevel { + void display(String voltageLevelId); +} diff --git a/single-line-diagram-view/src/main/java/com/powsybl/sld/view/NodeHandler.java b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/NodeHandler.java new file mode 100644 index 000000000..8f5738f95 --- /dev/null +++ b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/NodeHandler.java @@ -0,0 +1,169 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.view; + +import com.powsybl.sld.library.ComponentSize; +import com.powsybl.sld.model.BaseNode; +import com.powsybl.sld.model.BusCell; +import com.powsybl.sld.svg.GraphMetadata; +import javafx.scene.Node; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static com.powsybl.sld.library.ComponentTypeName.LINE; +import static com.powsybl.sld.library.ComponentTypeName.TWO_WINDINGS_TRANSFORMER; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + * @author Franck Lecuyer + */ +public class NodeHandler implements BaseNode { + + private final Node node; + + private String componentType; + + private Double rotationAngle; + + private final List wireHandlers = new ArrayList<>(); + + private final GraphMetadata metadata; + + private final String vId; + private final String nextVId; + + private double mouseX; + private double mouseY; + + private double screenX; + private double screenY; + + private BusCell.Direction direction; + + private DisplayVoltageLevel displayVL; + + public NodeHandler(Node node, String componentType, Double rotationAngle, + GraphMetadata metadata, + String vId, String nextVId, BusCell.Direction direction) { + this.node = Objects.requireNonNull(node); + this.componentType = componentType; + this.rotationAngle = rotationAngle; + this.metadata = Objects.requireNonNull(metadata); + this.vId = Objects.requireNonNull(vId); + this.nextVId = nextVId; + this.direction = direction; + + setDragAndDrop(); + } + + public Node getNode() { + return node; + } + + @Override + public String getId() { + return node.getId(); + } + + public String getVId() { + return vId; + } + + public String getNextVId() { + return nextVId; + } + + public BusCell.Direction getDirection() { + return direction; + } + + @Override + public String getComponentType() { + return componentType; + } + + @Override + public Double getRotationAngle() { + return rotationAngle; + } + + public void addWire(WireHandler w) { + wireHandlers.add(w); + } + + public List getWireHandlers() { + return wireHandlers; + } + + @Override + public boolean isRotated() { + return rotationAngle != null; + } + + public void setDisplayVL(DisplayVoltageLevel displayVL) { + this.displayVL = displayVL; + } + + @Override + public double getX() { + ComponentSize size = componentType != null + ? metadata.getComponentMetadata(componentType).getSize() + : new ComponentSize(0, 0); + return node.localToParent(node.getLayoutX() + size.getWidth() / 2, + node.getLayoutY() + size.getHeight() / 2).getX(); + } + + @Override + public double getY() { + ComponentSize size = componentType != null + ? metadata.getComponentMetadata(componentType).getSize() + : new ComponentSize(0, 0); + return node.localToParent(node.getLayoutX() + size.getWidth() / 2, + node.getLayoutY() + size.getHeight() / 2).getY(); + } + + public void setDragAndDrop() { + node.setOnMousePressed(event -> { + screenX = event.getScreenX(); + screenY = event.getScreenY(); + mouseX = event.getSceneX() - node.getTranslateX(); + mouseY = event.getSceneY() - node.getTranslateY(); + event.consume(); + }); + + node.setOnMouseDragged(event -> { + translate(event.getSceneX() - mouseX, event.getSceneY() - mouseY); + event.consume(); + }); + + node.setOnMouseReleased(event -> { + if (event.getScreenX() == screenX && + event.getScreenY() == screenY && + componentType.equals(LINE) || componentType.equals(TWO_WINDINGS_TRANSFORMER)) { + displayNextVoltageLevel(); + } + }); + } + + public void translate(double translateX, double translateY) { + node.setTranslateX(translateX); + node.setTranslateY(translateY); + for (WireHandler w : wireHandlers) { + w.refresh(); + } + } + + private void displayNextVoltageLevel() { + if (nextVId != null) { + displayVL.display(nextVId); + } + } +} diff --git a/single-line-diagram-view/src/main/java/com/powsybl/sld/view/SubstationDiagramView.java b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/SubstationDiagramView.java new file mode 100644 index 000000000..335a92681 --- /dev/null +++ b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/SubstationDiagramView.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.view; + +import javafx.scene.Group; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.InputStream; +import java.util.Objects; + +/** + * @author Franck Lecuyer + */ +public final class SubstationDiagramView extends AbstractContainerDiagramView { + + private static final Logger LOGGER = LoggerFactory.getLogger(SubstationDiagramView.class); + + private SubstationDiagramView(Group svgImage) { + super(svgImage); + } + + public static SubstationDiagramView load(InputStream svgInputStream, + InputStream metadataInputStream, + DisplayVoltageLevel displayVL) { + Objects.requireNonNull(svgInputStream); + Objects.requireNonNull(metadataInputStream); + + Group svgImage = loadSvgAndMetadata(svgInputStream, metadataInputStream, displayVL); + + return new SubstationDiagramView(svgImage); + } +} diff --git a/single-line-diagram-view/src/main/java/com/powsybl/sld/view/VoltageLevelDiagramView.java b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/VoltageLevelDiagramView.java new file mode 100644 index 000000000..ee3dce21c --- /dev/null +++ b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/VoltageLevelDiagramView.java @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.view; + +import javafx.scene.Group; + +import java.io.InputStream; +import java.util.Objects; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public final class VoltageLevelDiagramView extends AbstractContainerDiagramView { + private VoltageLevelDiagramView(Group svgImage) { + super(svgImage); + } + + public static VoltageLevelDiagramView load(InputStream svgInputStream, + InputStream metadataInputStream, + DisplayVoltageLevel displayVL) { + Objects.requireNonNull(svgInputStream); + Objects.requireNonNull(metadataInputStream); + + Group svgImage = loadSvgAndMetadata(svgInputStream, metadataInputStream, displayVL); + + return new VoltageLevelDiagramView(svgImage); + } +} diff --git a/single-line-diagram-view/src/main/java/com/powsybl/sld/view/VoltageLevelHandler.java b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/VoltageLevelHandler.java new file mode 100644 index 000000000..b844777af --- /dev/null +++ b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/VoltageLevelHandler.java @@ -0,0 +1,198 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.view; + +import com.powsybl.sld.layout.HorizontalSubstationLayout; +import com.powsybl.sld.layout.InfoCalcPoints; +import com.powsybl.sld.layout.LayoutParameters; +import com.powsybl.sld.library.ComponentSize; +import com.powsybl.sld.model.BaseNode; +import com.powsybl.sld.model.BusCell; +import com.powsybl.sld.model.Coord; +import com.powsybl.sld.svg.GraphMetadata; +import javafx.scene.Node; +import javafx.scene.shape.Polyline; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.powsybl.sld.library.ComponentTypeName.BUSBAR_SECTION; + +/** + * @author Franck Lecuyer + */ +public class VoltageLevelHandler implements BaseNode { + + private final Node node; // node for voltageLevel label + + private final List nodeHandlers = new ArrayList<>(); + + private final String vId; + + private double mouseX; + private double mouseY; + + private final GraphMetadata metadata; + + public VoltageLevelHandler(Node node, GraphMetadata metadata, String vId) { + this.node = Objects.requireNonNull(node); + this.metadata = Objects.requireNonNull(metadata); + this.vId = Objects.requireNonNull(vId); + + setDragAndDrop(); + } + + public Node getNode() { + return node; + } + + @Override + public String getId() { + return node.getId(); + } + + public String getVId() { + return vId; + } + + @Override + public String getComponentType() { + return null; + } + + @Override + public Double getRotationAngle() { + return null; + } + + @Override + public boolean isRotated() { + return false; + } + + @Override + public double getX() { + ComponentSize size = new ComponentSize(0, 0); + return node.localToParent(node.getLayoutX() + size.getWidth() / 2, + node.getLayoutY() + size.getHeight() / 2).getX(); + } + + @Override + public double getY() { + ComponentSize size = new ComponentSize(0, 0); + return node.localToParent(node.getLayoutX() + size.getWidth() / 2, + node.getLayoutY() + size.getHeight() / 2).getY(); + } + + public void addNodeHandlers(List nodeHandlers) { + this.nodeHandlers.addAll(nodeHandlers); + } + + public void setDragAndDrop() { + node.setOnMousePressed(event -> { + mouseX = event.getSceneX() - node.getTranslateX(); + mouseY = event.getSceneY() - node.getTranslateY(); + event.consume(); + }); + + node.setOnMouseDragged(event -> { + // apply transformation for label node + node.setTranslateX(event.getSceneX() - mouseX); + node.setTranslateY(event.getSceneY() - mouseY); + + // apply transformation to all nodes of the voltageLevel in nodeHandlers list + nodeHandlers.stream().filter(n -> n.getVId().equals(vId)).forEach(v -> v.translate(event.getSceneX() - mouseX, + event.getSceneY() - mouseY)); + + // redraw the snakeLines between the voltage levels + redrawSnakeLines(); + + event.consume(); + }); + } + + private void redrawSnakeLines() { + // redraw the snakeLines between the voltage levels + // + Map nbSnakeLinesTopBottom = EnumSet.allOf(BusCell.Direction.class).stream().collect(Collectors.toMap(Function.identity(), v -> 0)); + Map nbSnakeLinesBetween = new HashMap<>(); + + Map posVL = new HashMap<>(); + + List whSnakeLines = new ArrayList<>(); + + for (NodeHandler nh : nodeHandlers) { + if (nh.getComponentType() != null && nh.getComponentType().equals(BUSBAR_SECTION)) { + if (!posVL.containsKey(nh.getVId())) { + posVL.put(nh.getVId(), new Coord(Double.MAX_VALUE, 0)); + } + double x = Math.min(posVL.get(nh.getVId()).getX(), nh.getX()); + posVL.put(nh.getVId(), new Coord(x, 0)); + } + + for (WireHandler wh : nh.getWireHandlers()) { + if (wh.isSnakeLine()) { + whSnakeLines.add(wh); + } + } + nbSnakeLinesBetween.put(nh.getVId(), 0); + } + + for (WireHandler wh : whSnakeLines) { + List pol = calculatePolylineSnakeLine(metadata.getLayoutParameters(), + wh, + posVL, + nbSnakeLinesTopBottom, + nbSnakeLinesBetween); + ((Polyline) wh.getNode()).getPoints().setAll(pol); + } + } + + private List calculatePolylineSnakeLine(LayoutParameters layoutParam, + WireHandler wh, + Map posVL, + Map nbSnakeLinesTopBottom, + Map nbSnakeLinesBetween) { + NodeHandler nh1 = wh.getNodeHandler1(); + NodeHandler nh2 = wh.getNodeHandler2(); + + BusCell.Direction dNode1 = nh1.getDirection(); + BusCell.Direction dNode2 = nh2.getDirection(); + + double xMaxGraph; + String idMaxGraph; + + if (posVL.get(nh1.getVId()).getX() > posVL.get(nh2.getVId()).getX()) { + xMaxGraph = posVL.get(nh1.getVId()).getX(); + idMaxGraph = nh1.getVId(); + } else { + xMaxGraph = posVL.get(nh2.getVId()).getX(); + idMaxGraph = nh2.getVId(); + } + + double x1 = nh1.getX(); + double y1 = nh1.getY(); + double x2 = nh2.getX(); + double y2 = nh2.getY(); + + InfoCalcPoints info = new InfoCalcPoints(); + info.setLayoutParam(layoutParam); + info.setdNode1(dNode1); + info.setdNode2(dNode2); + info.setNbSnakeLinesTopBottom(nbSnakeLinesTopBottom); + info.setNbSnakeLinesBetween(nbSnakeLinesBetween); + info.setX1(x1); + info.setX2(x2); + info.setY1(y1); + info.setY2(y2); + info.setxMaxGraph(xMaxGraph); + info.setIdMaxGraph(idMaxGraph); + + return HorizontalSubstationLayout.calculatePolylinePoints(info); + } +} diff --git a/single-line-diagram-view/src/main/java/com/powsybl/sld/view/WireHandler.java b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/WireHandler.java new file mode 100644 index 000000000..877b4321a --- /dev/null +++ b/single-line-diagram-view/src/main/java/com/powsybl/sld/view/WireHandler.java @@ -0,0 +1,207 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.view; + +import com.google.common.collect.ImmutableList; +import com.powsybl.sld.library.ComponentSize; +import com.powsybl.sld.svg.GraphMetadata; +import com.powsybl.sld.svg.WireConnection; +import javafx.geometry.Point2D; +import javafx.scene.Group; +import javafx.scene.Node; +import javafx.scene.shape.Polyline; +import javafx.scene.transform.Rotate; +import javafx.scene.transform.Translate; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.powsybl.sld.library.ComponentTypeName.ARROW; + +/** + * @author Benoit Jeanson + * @author Nicolas Duchene + * @author Geoffroy Jamgotchian + */ +public class WireHandler { + + private final Polyline node; + + private final List arrows = new ArrayList<>(); + + private final NodeHandler nodeHandler1; + + private final NodeHandler nodeHandler2; + + private final boolean straight; + + private final boolean snakeLine; + + private final GraphMetadata metadata; + + public WireHandler(Polyline node, NodeHandler nodeHandler1, NodeHandler nodeHandler2, + boolean straight, boolean snakeLine, GraphMetadata metadata) { + this.node = Objects.requireNonNull(node); + this.nodeHandler1 = Objects.requireNonNull(nodeHandler1); + this.nodeHandler2 = Objects.requireNonNull(nodeHandler2); + this.straight = straight; + this.snakeLine = snakeLine; + this.metadata = Objects.requireNonNull(metadata); + } + + public Node getNode() { + return this.node; + } + + public boolean isSnakeLine() { + return snakeLine; + } + + public NodeHandler getNodeHandler1() { + return nodeHandler1; + } + + public NodeHandler getNodeHandler2() { + return nodeHandler2; + } + + public void addArrow(Group g) { + arrows.add(g); + } + + public void refresh() { + + WireConnection wireConnection = WireConnection.searchBetterAnchorPoints(metadata, nodeHandler1, nodeHandler2); + + List pol = wireConnection.calculatePolylinePoints(nodeHandler1, nodeHandler2, straight); + node.getPoints().setAll(pol); + + relocateArrows(); + } + + private void relocateArrows() { + for (int i = 0; i < arrows.size(); i++) { + relocateArrow(node, arrows.get(i), i); + } + } + + private void relocateArrow(Polyline polyline, Group arrow, int arrowNum) { + ComponentSize arrowSize = metadata.getComponentMetadata(ARROW).getSize(); + Point2D center = new Point2D(arrowSize.getWidth() / 2, arrowSize.getHeight() / 2); + double distance = metadata.getArrowMetadata(arrow.getId()).getDistance() + arrowNum * arrowSize.getHeight() * 2; + relocateArrow(polyline, arrow, center, distance); + } + + + /** + * A position in the 2D plane together with an orientation + */ + static class OrientedPosition { + + private final Point2D point; + private final double orientation; + + public Point2D getPoint() { + return point; + } + + public double getOrientation() { + return orientation; + } + + public OrientedPosition(Point2D point, double orientation) { + this.point = point; + this.orientation = orientation; + } + } + + private static void relocateArrow(Polyline polyline, Group arrow, Point2D center, double distance) { + OrientedPosition newCenterPosition = positionAtDistance(getPoints(polyline), distance); + Point2D newCenterPoint = newCenterPosition.getPoint().subtract(center); + + arrow.getTransforms().clear(); + + arrow.getTransforms().add(new Translate(newCenterPoint.getX(), newCenterPoint.getY())); + arrow.getChildren().forEach(c -> { + if (c instanceof Group) { + c.getTransforms().clear(); + c.getTransforms().add(new Rotate(newCenterPosition.getOrientation(), center.getX(), center.getY())); + } else if (c instanceof javafx.scene.text.Text) { + c.getTransforms().clear(); + if (newCenterPosition.getOrientation() != 180) { + c.getTransforms().add(new Rotate(newCenterPosition.getOrientation(), center.getX(), center.getY())); + } + } + }); + } + + private static boolean isEven(int number) { + return (number & 1) == 0; + } + + private static List getPoints(Polyline line) { + List points = new ArrayList<>(); + List coordinates = line.getPoints(); + checkArgument(isEven(coordinates.size()), "Number of coordinates of polyline points should be even."); + + for (int i = 0; i < coordinates.size(); i = i + 2) { + points.add(new Point2D(coordinates.get(i), coordinates.get(i + 1))); + } + return ImmutableList.copyOf(points); + } + + /** + * Computes the position at the specified distance on the line represented by a list of points, + * from the starting point to the end point. + */ + static OrientedPosition positionAtDistance(List points, double distance) { + double residual = distance; + + if (points.size() < 2) { + throw new IllegalArgumentException("Line must contain at least one segment"); + } + + Point2D start = null; + Point2D end = null; + for (int i = 0; i < points.size() - 1; i++) { + start = points.get(i); + end = points.get(i + 1); + + double segmentLength = start.distance(end); + + if (segmentLength < residual) { + residual -= segmentLength; + } else { + return positionAtRatio(start, end, residual / segmentLength); + } + } + + return positionAtRatio(start, end, 1f); + } + + /** + * Computes the position at the specified ratio from the starting point to the end point. + */ + private static OrientedPosition positionAtRatio(Point2D start, Point2D end, double ratio) { + Point2D segment = end.subtract(start); + Point2D point = start.add(end.subtract(start).multiply(ratio)); + double angle = orientedAngle(new Point2D(0, 1), segment); + return new OrientedPosition(point, angle); + } + + /** + * Computes the oriented angle between 2 segments, in degrees. + */ + private static double orientedAngle(Point2D segment1, Point2D segment2) { + double x = segment1.dotProduct(segment2); + double y = segment1.crossProduct(segment2).dotProduct(0, 0, 1); + return Math.toDegrees(Math.atan2(y, x)); + } + +} diff --git a/single-line-diagram-view/src/main/resources/log4j2.xml b/single-line-diagram-view/src/main/resources/log4j2.xml new file mode 100644 index 000000000..9f905c97d --- /dev/null +++ b/single-line-diagram-view/src/main/resources/log4j2.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/single-line-diagram-view/src/main/resources/logback.xml b/single-line-diagram-view/src/main/resources/logback.xml new file mode 100644 index 000000000..99f35c621 --- /dev/null +++ b/single-line-diagram-view/src/main/resources/logback.xml @@ -0,0 +1,11 @@ + + + + + %d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + diff --git a/single-line-diagram-view/src/test/java/com/powsybl/sld/view/WireHandlerTest.java b/single-line-diagram-view/src/test/java/com/powsybl/sld/view/WireHandlerTest.java new file mode 100644 index 000000000..a3763132b --- /dev/null +++ b/single-line-diagram-view/src/test/java/com/powsybl/sld/view/WireHandlerTest.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2019, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.sld.view; + +import com.google.common.collect.ImmutableList; +import com.powsybl.sld.view.WireHandler.OrientedPosition; +import javafx.geometry.Point2D; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.*; + +/** + * @author Sylvain Leclerc + */ +public class WireHandlerTest { + + @Test + public void computePointAtDistance() { + + List points = ImmutableList.of(new Point2D(1, 0), + new Point2D(1, 1), + new Point2D(2, 1), + new Point2D(2, 2), + new Point2D(1, 2)); + + OrientedPosition middle1 = WireHandler.positionAtDistance(points, 0.5); + assertEquals(new Point2D(1, 0.5), middle1.getPoint()); + assertEquals(0, middle1.getOrientation(), 0); + + OrientedPosition middle2 = WireHandler.positionAtDistance(points, 1.5); + assertEquals(new Point2D(1.5, 1), middle2.getPoint()); + assertEquals(-90, middle2.getOrientation(), 0); + + OrientedPosition middle3 = WireHandler.positionAtDistance(points, 2.5); + assertEquals(new Point2D(2, 1.5), middle3.getPoint()); + assertEquals(0, middle3.getOrientation(), 0); + + OrientedPosition middle4 = WireHandler.positionAtDistance(points, 3.5); + assertEquals(new Point2D(1.5, 2), middle4.getPoint()); + assertEquals(90, middle4.getOrientation(), 0); + } + + @Test + public void computeAngle() { + List points1 = ImmutableList.of(new Point2D(0, 0), new Point2D(1, 1)); + OrientedPosition position1 = WireHandler.positionAtDistance(points1, 0.5); + assertEquals(-45, position1.getOrientation(), 1e-5); + + List points2 = ImmutableList.of(new Point2D(0, 0), new Point2D(-1, 1)); + OrientedPosition position2 = WireHandler.positionAtDistance(points2, 0.5); + assertEquals(45, position2.getOrientation(), 1e-5); + + List points3 = ImmutableList.of(new Point2D(0, 0), new Point2D(-1, -1)); + OrientedPosition position3 = WireHandler.positionAtDistance(points3, 0.5); + assertEquals(135, position3.getOrientation(), 1e-5); + + List points4 = ImmutableList.of(new Point2D(0, 0), new Point2D(1, -1)); + OrientedPosition position4 = WireHandler.positionAtDistance(points4, 0.5); + assertEquals(-135, position4.getOrientation(), 1e-5); + } +}