Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ If this is a leaf node (for instance `ACLineSegment`), it "just works". If you w
class higher in the hierarchy (for instance `Equipment`) there is a lot more work to do.

```python
from pydantic import Field
from pydantic.dataclasses import dataclass

from pycgmes.resources.Bay import Bay

@dataclass
class CustomBay(Bay):
colour: str = Field(
Expand All @@ -82,9 +87,11 @@ class CustomBay(Bay):
],
"is_used": True,
"is_class_attribute": False,
"is_datatype_attribute": False,
"is_enum_attribute": False,
"is_list_attribute": False,
"is_primitive_attribute": True,
"attribute_class": "String",
},
)
```
Expand Down Expand Up @@ -118,10 +125,11 @@ class CustomBayAttr(Bay):
],
"is_used": True,
"is_class_attribute": False,
"is_datatype_attribute": False,
"is_enum_attribute": False,
"is_list_attribute": False,
"is_primitive_attribute": True,
"namespace": "custom",
"attribute_class": "String",
},
)

Expand All @@ -138,8 +146,6 @@ By default, an attribute is fully qualified. A standard `attribute` in `ACLineSe
In the case of a custom attribute defined via a sub class, the result would be: `ACLineSegmentCustom.customAttribute`. To preserve the original class name (i.e. serialise your attribute as `ACLineSegment.customAttribute`), you need to override the `apparent_name` of your custom class:

```python
from pydantic.dataclasses import dataclass

from pycgmes.resources.ACLineSegment import ACLineSegment

@dataclass
Expand Down Expand Up @@ -176,37 +182,38 @@ The namespace of an attribute is the first value found:
- namespace of the first parent defining one. The top parent (`Base`) defined `cim`.

```python
from pydantic import Field
from pydantic.dataclasses import dataclass

from pycgmes.resources.ACLineSegment import ACLineSegment

@dataclass
class ACLineSegmentCustom(ACLineSegment):
colour: str = Field(
default="Red",
json_schema_extra={
"in_profiles": [
Profile.EQ, # Do not do this, see chapter "Create a new profile" ],
Profile.EQ, # Do not do this, see chapter "Create a new profile"
],
"is_used": True,
"namespace": "custom",
"is_class_attribute": False,
"is_datatype_attribute": False,
"is_enum_attribute": False,
"is_list_attribute": False,
"is_primitive_attribute": True,
"namespace": "custom",
"attribute_class": "String",
},
)

size: str = Field(
default="Big",
json_schema_extra={
"in_profiles": [
Profile.EQ, # Do not do this, see chapter "Create a new profile" ],
Profile.EQ, # Do not do this, see chapter "Create a new profile"
],
"is_used": True,
"is_class_attribute": False,
"is_datatype_attribute": False,
"is_enum_attribute": False,
"is_list_attribute": False,
"is_primitive_attribute": True,
"attribute_class": "String",
},
)

Expand Down
4 changes: 2 additions & 2 deletions SConstruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from SCons.Script import COMMAND_LINE_TARGETS

_CHECK_ONLY = "check" in COMMAND_LINE_TARGETS
_SUBJECT = "pycgmes"
_SUBJECT = "pycgmes examples"
_TEST_SUBJECT = "tests"
_RESOURCES = "pycgmes/resources"

Expand Down Expand Up @@ -43,7 +43,7 @@ def _exec(command: str, env: Mapping | None = None) -> int:
# Formatting targets, which might change files. Let's run them *before* the linters and friends.
# This is why ruff is the first of the quality target, as it fixes things as well.
if "format" in COMMAND_LINE_TARGETS:
cmd = f"ruff format SConstruct.py {_SUBJECT} {_TEST_SUBJECT} examples --exclude {_RESOURCES}"
cmd = f"ruff format SConstruct.py {_SUBJECT} {_TEST_SUBJECT} --exclude {_RESOURCES}"
if _CHECK_ONLY:
cmd += " --diff"
_exec(cmd)
Expand Down
28 changes: 15 additions & 13 deletions examples/writer_example.py → examples/chevron_writer_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: Apache-2.0

import os
from pathlib import Path

from pycgmes.resources.Analog import Analog
from pycgmes.resources.AnalogValue import AnalogValue
Expand All @@ -12,23 +12,24 @@
from pycgmes.resources.TopologicalNode import TopologicalNode
from pycgmes.resources.VoltageLevel import VoltageLevel
from pycgmes.utils.base import Base
from pycgmes.utils.writer import Writer
from pycgmes.utils.chevron_writer import ChevronWriter
from pycgmes.utils.profile import Profile

_curr_dir = os.path.dirname(os.path.realpath(__file__))
_curr_dir = Path(__file__).resolve().parent


def main():
output_dir = _curr_dir + "/output"
os.makedirs(output_dir, exist_ok=True)
def main() -> None:
output_dir = _curr_dir / "output"
output_dir.mkdir(parents=True, exist_ok=True)
objects = {
"BaseVoltage.20": BaseVoltage(mRID="BaseVoltage.20", nominalVoltage=20.0),

Check failure on line 25 in examples/chevron_writer_example.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "BaseVoltage.20" 4 times.

See more on https://sonarcloud.io/project/issues?id=alliander-opensource_pycgmes&issues=AZ9aJAnYzj6xL1YhysDB&open=AZ9aJAnYzj6xL1YhysDB&pullRequest=44
"VoltageLevel.98": VoltageLevel(mRID="VoltageLevel.98", name="98", BaseVoltage="BaseVoltage.20"),

Check failure on line 26 in examples/chevron_writer_example.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "VoltageLevel.98" 3 times.

See more on https://sonarcloud.io/project/issues?id=alliander-opensource_pycgmes&issues=AZ9aJAnYzj6xL1YhysDA&open=AZ9aJAnYzj6xL1YhysDA&pullRequest=44
"VoltageLevel.99": VoltageLevel(mRID="VoltageLevel.99", name="99", BaseVoltage="BaseVoltage.20"),

Check failure on line 27 in examples/chevron_writer_example.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "VoltageLevel.99" 3 times.

See more on https://sonarcloud.io/project/issues?id=alliander-opensource_pycgmes&issues=AZ9aJAnYzj6xL1YhysC_&open=AZ9aJAnYzj6xL1YhysC_&pullRequest=44
"N0": TopologicalNode(mRID="N0", name="N0", ConnectivityNodeContainer="VoltageLevel.98"),
"N1": TopologicalNode(mRID="N1", name="N1", ConnectivityNodeContainer="VoltageLevel.99"),
"TopologicalIsland.N": TopologicalIsland(mRID="N", name="N", TopologicalNodes=["N0", "N1"]),
"Terminal.N0": Terminal(mRID="Terminal.N0", name="Terminal at N0", TopologicalNode="N0"),
"N": TopologicalIsland(mRID="N", name="N", TopologicalNodes=["N0", "N1"]),
"Terminal.N0": Terminal(mRID="Terminal.N0", name="Terminal at N0", TopologicalNode="N0", connected=True),

Check failure on line 31 in examples/chevron_writer_example.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "Terminal.N0" 3 times.

See more on https://sonarcloud.io/project/issues?id=alliander-opensource_pycgmes&issues=AZ9aJAnYzj6xL1YhysDC&open=AZ9aJAnYzj6xL1YhysDC&pullRequest=44
"Analog.N0.Voltage": Analog(

Check failure on line 32 in examples/chevron_writer_example.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "Analog.N0.Voltage" 3 times.

See more on https://sonarcloud.io/project/issues?id=alliander-opensource_pycgmes&issues=AZ9aJAnYzj6xL1YhysC-&open=AZ9aJAnYzj6xL1YhysC-&pullRequest=44
mRID="Analog.N0.Voltage",
name="Voltage Magnitude Measurement at N0",
shortName="N0",
Expand All @@ -39,13 +40,14 @@
),
"AnalogValue.N0.Voltage": AnalogValue(mRID="AnalogValue.N0.Voltage", Analog="Analog.N0.Voltage"),
}
write(output_dir + "/Example_Model", "Example_Model", objects)
write(output_dir / "Example_Model", "Example_Model", objects)


def write(outputfile: str, model_id: str, objects: dict[str, Base]):
writer = Writer(objects)
class_profile_map = Writer.get_class_profile_map(writer.objects.values())
profile_file_map = writer.write(outputfile, model_id, class_profile_map)
def write(outputfile: Path, model_id: str, objects: dict[str, Base]) -> None:
writer = ChevronWriter(objects)
class_profile_map = ChevronWriter.get_class_profile_map(writer.objects.values())
class_profile_map["Terminal"] = Profile.TP # override recommended profile
profile_file_map = writer.write(str(outputfile), model_id, class_profile_map)
for idx, (profile, file) in enumerate(profile_file_map.items()):
print(f"CIM outputfile {idx + 1} for {profile}: {file}")

Expand Down
10 changes: 7 additions & 3 deletions examples/custom_attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@ class CustomAttribute(ACLineSegment):
CustomProfile.MYOWN,
],
"is_used": True,
"namespace": "custom for colour",
"is_class_attribute": False,
"is_datatype_attribute": False,
"is_enum_attribute": False,
"is_list_attribute": False,
"is_primitive_attribute": True,
"namespace": "custom for colour",
"attribute_class": "String",
},
)

Expand All @@ -41,16 +43,18 @@ class CustomAttribute(ACLineSegment):
],
"is_used": True,
"is_class_attribute": False,
"is_datatype_attribute": False,
"is_enum_attribute": False,
"is_list_attribute": False,
"is_primitive_attribute": True,
"attribute_class": "String",
},
# No namespace defined, it will use the class namespace.
)

@classmethod
def apparent_name(cls) -> str:
return cls.__base__.apparent_name()
return cls.__base__.apparent_name() # type: ignore

@cached_property
def namespace(self) -> str:
Expand All @@ -63,7 +67,7 @@ def namespace(self) -> str:
print("Attributes in profile MYOWN:")
print(
json.dumps(
{qualname: attr for qualname, attr in my_resource.cgmes_attributes_in_profile(CustomProfile.MYOWN).items()},
my_resource.cgmes_attributes_in_profile(CustomProfile.MYOWN),
indent=2,
)
)
23 changes: 22 additions & 1 deletion pycgmes/resources/ACDCConverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": False,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": False,
"is_enum_attribute": False,
Expand All @@ -80,6 +81,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": True,
"is_datatype_attribute": False,
"is_enum_attribute": False,
Expand All @@ -95,6 +97,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -111,6 +114,7 @@ class ACDCConverter(ConductingEquipment):
Profile.SV,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -127,6 +131,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -143,6 +148,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -159,6 +165,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -175,6 +182,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -191,6 +199,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -207,6 +216,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": False,
"is_enum_attribute": False,
Expand All @@ -223,6 +233,7 @@ class ACDCConverter(ConductingEquipment):
Profile.SSH,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -239,6 +250,7 @@ class ACDCConverter(ConductingEquipment):
Profile.SV,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -255,6 +267,7 @@ class ACDCConverter(ConductingEquipment):
Profile.SSH,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -271,6 +284,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -287,6 +301,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -303,6 +318,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -319,6 +335,7 @@ class ACDCConverter(ConductingEquipment):
Profile.SSH,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -335,6 +352,7 @@ class ACDCConverter(ConductingEquipment):
Profile.SSH,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -351,6 +369,7 @@ class ACDCConverter(ConductingEquipment):
Profile.SV,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -367,6 +386,7 @@ class ACDCConverter(ConductingEquipment):
Profile.SV,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -383,6 +403,7 @@ class ACDCConverter(ConductingEquipment):
Profile.EQ,
],
"is_used": True,
"namespace": "http://iec.ch/TC57/CIM100#", # NOSONAR
"is_class_attribute": False,
"is_datatype_attribute": True,
"is_enum_attribute": False,
Expand All @@ -399,8 +420,8 @@ def possible_profiles(self) -> set[BaseProfile]:
where this element can be found.
"""
return {
Profile.DY,
Profile.EQ,
Profile.DY,
Profile.SSH,
Profile.SV,
}
Expand Down
Loading
Loading