Commit d6366c93 authored by Philipp Reuber's avatar Philipp Reuber
Browse files

support references to URLs, modify tests

parent d97db9b9
Pipeline #234522 passed with stages
in 1 minute and 48 seconds
......@@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
# This function gets all attributes of an object and resolves references to other objects
def _get_class_attributes_with_references(res, version):
def _get_class_attributes_with_references(res, version, url_reference_dict):
class_attributes_list = []
for key in res.keys():
......@@ -20,7 +20,7 @@ def _get_class_attributes_with_references(res, version):
attributes_dict = _get_attributes(res[key])
# change attribute references to mRID of the object, res needed because classes like SvPowerFlow does not have
# mRID as an attribute. Therefore the corresponding class has to be searched in the res dictionary
class_dict['attributes'] = _get_reference_uuid(attributes_dict, version, res, key)
class_dict['attributes'] = _get_reference_uuid(attributes_dict, version, res, key, url_reference_dict)
class_attributes_list.append(class_dict)
del class_dict
......@@ -28,7 +28,7 @@ def _get_class_attributes_with_references(res, version):
# This function resolves references to objects
def _get_reference_uuid(attr_dict, version, res, mRID):
def _get_reference_uuid(attr_dict, version, res, mRID, url_reference_dict):
reference_list = []
base_class_name = 'cimpy.' + version + '.Base'
base_module = importlib.import_module(base_class_name)
......@@ -77,7 +77,13 @@ def _get_reference_uuid(attr_dict, version, res, mRID):
elif attr_dict[key] == "" or attr_dict[key] is None:
pass
else:
attributes['value'] = attr_dict[key]
if mRID in url_reference_dict.keys():
if key.split('.')[1] in url_reference_dict[mRID]:
attributes['value'] = '%URL%' + url_reference_dict[mRID][key.split('.')[1]][attr_dict[key]]
else:
attributes['value'] = attr_dict[key]
else:
attributes['value'] = attr_dict[key]
attributes['attr_name'] = key
if 'value' in attributes.keys():
......@@ -108,7 +114,10 @@ def _set_attribute_or_reference(text, render):
result = result.split('@')
value = result[0]
attr_name = result[1]
if '%' in value:
if '%URL%' in value:
reference = value.split('%')[2]
return ' rdf:resource="' + reference + '"/>'
elif '%' in value:
reference = value.split('%')[1]
return ' rdf:resource="#' + reference + '"/>'
else:
......@@ -271,7 +280,7 @@ def _sort_classes_to_profile(class_attributes_list, activeProfileList):
return export_dict, export_about_dict
def cim_export(res, namespaces_dict, file_name, version, activeProfileList):
def cim_export(res, namespaces_dict, file_name, version, activeProfileList, url_reference_dict={}):
"""Function for serialization of cgmes classes
This function serializes cgmes classes with the template engine chevron. The classes are separated by their profile
......@@ -284,6 +293,10 @@ def cim_export(res, namespaces_dict, file_name, version, activeProfileList):
:param file_name: a string with the name of the xml files which will be created
:param version: cgmes version, e.g. version = "cgmes_v2_4_15"
:param activeProfileList: a list containing the strings of all short names of the profiles used for serialization
:param: url_reference_dict: a map containing a mapping between references to URLs and the extracted value of the
URL, e.g. 'absoluteValue': 'http://iec.ch/TC57/2012/CIM-schema-cim16#OperationalLimitDirectionKind.absoluteValue'
These mappings are accessible via the mRID of the class and the name of the attribute, e.g.
url_reference_dict[mRID][attribute_name] = {mapping like example above}
"""
cwd = os.getcwd()
......@@ -292,7 +305,7 @@ def cim_export(res, namespaces_dict, file_name, version, activeProfileList):
logger.info('Start export procedure.')
# returns all classes with their attributes and resolved references
class_attributes_list = _get_class_attributes_with_references(res, version)
class_attributes_list = _get_class_attributes_with_references(res, version, url_reference_dict)
# determine class and attribute export profiles. The export dict contains all classes and their attributes where
# the class definition and the attribute definitions are in the same profile. Every entry in about_dict generates
......
......@@ -23,6 +23,10 @@ def cim_import(xml_files, cgmes_version, start_dict=None):
* If start_dict=None the complete file will be read
:return: res: map containing all classes contained in the xml file(s), assessable via the mRID
:return: namespaces: a map containing all RDF namespaces
:return: url_reference_dict: a map containing a mapping between references to URLs and the extracted value of the
URL, e.g. 'absoluteValue': 'http://iec.ch/TC57/2012/CIM-schema-cim16#OperationalLimitDirectionKind.absoluteValue'
These mappings are accessible via the mRID of the class and the name of the attribute, e.g.
url_reference_dict[mRID][attribute_name] = {mapping like example above}
"""
# Import cim version class
......@@ -50,7 +54,7 @@ def cim_import(xml_files, cgmes_version, start_dict=None):
res, logger_errors_grouped, logger_info_grouped = _instantiate_classes(res, xml_files, cgmes_version_path,
namespace_rdf, base, logger_errors_grouped,
logger_info_grouped)
res, logger_errors_grouped = _set_attributes(res, xml_files, namespace_rdf, base,
res, url_reference_dict, logger_errors_grouped = _set_attributes(res, xml_files, namespace_rdf, base,
logger_errors_grouped)
if logger_errors_grouped:
......@@ -71,7 +75,7 @@ def cim_import(xml_files, cgmes_version, start_dict=None):
# print info of how many classes in total were instantiated to terminal
print('Created totally {} CIM objects in {}s'.format(len(res), elapsed_time))
return res, namespaces
return res, namespaces, url_reference_dict
# This function instantiates the classes defined in all RDF files. All attributes are set to default values.
......@@ -169,6 +173,10 @@ def _instantiate_classes(res, xml_files, cgmes_version_path, namespace_rdf, base
# the attributes are read in the serializationProfile dictionary.
def _set_attributes(res, xml_files, namespace_rdf, base, logger_errors_grouped):
m = len(base)
# stores a mapping between references to URLs and the extracted attribute
url_reference_dict = {}
# Second step pass sets attributes and references.
for xml_file in xml_files:
......@@ -298,6 +306,10 @@ def _set_attributes(res, xml_files, namespace_rdf, base, logger_errors_grouped):
logger_errors_grouped[error_msg] = 1
else: # enum
# if http in uuid2 reference to URL, create mapping
if 'http' in uuid2:
url_reference_dict[uuid] = {attr: {uuid2.rsplit(".", 1)[1]: uuid2}}
# url_reference_dict[uuid2.rsplit(".", 1)[1]] = uuid2
val = uuid2.rsplit(".", 1)[1]
setattr(obj, attr, val)
......@@ -327,7 +339,7 @@ def _set_attributes(res, xml_files, namespace_rdf, base, logger_errors_grouped):
logger.info('END of parsing file "{}"'.format(xml_file))
return res, logger_errors_grouped
return res, url_reference_dict, logger_errors_grouped
# Returns a map of prefix to namespace for the given XML file.
......
......@@ -15,9 +15,9 @@ for file in xml_files:
# res = cimpy.cimread(xml_files)
res, namespaces = cimpy.cim_import(xml_files_abs, "cgmes_v2_4_15")
res, namespaces, url_reference_dict = cimpy.cim_import(xml_files_abs, "cgmes_v2_4_15")
activeProfileList = ['DI', 'EQ', 'SV', 'TP']
# dicts = cimpy.get_class_attributes_dict(res)
cimpy.cim_export(res, namespaces, 'Slack_Trafo_Load', 'cgmes_v2_4_15', activeProfileList)
cimpy.cim_export(res, namespaces, 'CIGREMV_reference_cgmes_v2_4_15', 'cgmes_v2_4_15', activeProfileList, url_reference_dict)
......@@ -15,7 +15,7 @@ for file in xml_files:
xml_files_abs.append(os.path.abspath(file))
# res = cimpy.cimread(xml_files)
res, _ = cimpy.cim_import(xml_files_abs, "cgmes_v2_4_15")
res, _, _ = cimpy.cim_import(xml_files_abs, "cgmes_v2_4_15")
print("\n\n")
results = ["ACLineSegment", "PowerTransformer", "EnergyConsumer"]
for key, value in res.items():
......
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cim="http://iec.ch/TC57/2012/CIM-schema-cim16#" xmlns:md="http://iec.ch/TC57/61970-552/ModelDescription/1#" xmlns:entsoe="http://entsoe.eu/Secretariat/ProfileExtension/2#" xmlns:neplan="http://www.neplan.ch#" >
<md:FullModel rdf:about="Test">
<md:Model.created>27/01/2020 18:45:27</md:Model.created>
<md:FullModel rdf:about="CIGREMV_reference_cgmes_v2_4_15">
<md:Model.created>29/01/2020 15:09:48</md:Model.created>
<md:Model.modelingAuthoritySet>www.acs.eonerc.rwth-aachen.de</md:Model.modelingAuthoritySet>
<md:Model.profile>DiagramLayout</md:Model.profile>
</md:FullModel>
<cim:Diagram rdf:ID="_3b336fc7-5b11-41a1-83e8-5509bbd4eda8">
<cim:IdentifiedObject.name>Load6</cim:IdentifiedObject.name>
<cim:Diagram.orientation>negative</cim:Diagram.orientation>
<cim:Diagram.orientation rdf:resource="http://iec.ch/TC57/2010/CIM-schema-cim16#OrientationKind.negative"/>
</cim:Diagram>
<cim:DiagramObject rdf:ID="_f71dc9cf-458e-4b79-9c3b-6e3a9b5cd398">
<cim:DiagramObject.Diagram rdf:resource="#_3b336fc7-5b11-41a1-83e8-5509bbd4eda8"/>
......
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cim="http://iec.ch/TC57/2012/CIM-schema-cim16#" xmlns:md="http://iec.ch/TC57/61970-552/ModelDescription/1#" xmlns:entsoe="http://entsoe.eu/Secretariat/ProfileExtension/2#" xmlns:neplan="http://www.neplan.ch#" >
<md:FullModel rdf:about="Test">
<md:Model.created>27/01/2020 18:45:27</md:Model.created>
<md:FullModel rdf:about="CIGREMV_reference_cgmes_v2_4_15">
<md:Model.created>29/01/2020 15:09:48</md:Model.created>
<md:Model.modelingAuthoritySet>www.acs.eonerc.rwth-aachen.de</md:Model.modelingAuthoritySet>
<md:Model.profile>Equipment</md:Model.profile>
</md:FullModel>
......@@ -171,12 +171,12 @@
<cim:OperationalLimitType rdf:ID="_d50aa23d-94a0-4462-a895-6bec6f7b89af">
<cim:IdentifiedObject.name>PATL</cim:IdentifiedObject.name>
<cim:OperationalLimitType.acceptableDuration>45000</cim:OperationalLimitType.acceptableDuration>
<cim:OperationalLimitType.direction>absoluteValue</cim:OperationalLimitType.direction>
<cim:OperationalLimitType.direction rdf:resource="http://iec.ch/TC57/2012/CIM-schema-cim16#OperationalLimitDirectionKind.absoluteValue"/>
</cim:OperationalLimitType>
<cim:OperationalLimitType rdf:ID="_182e44b2-641d-4502-b36d-1949f49a90ea">
<cim:IdentifiedObject.name>TATL</cim:IdentifiedObject.name>
<cim:OperationalLimitType.acceptableDuration>60</cim:OperationalLimitType.acceptableDuration>
<cim:OperationalLimitType.direction>absoluteValue</cim:OperationalLimitType.direction>
<cim:OperationalLimitType.direction rdf:resource="http://iec.ch/TC57/2012/CIM-schema-cim16#OperationalLimitDirectionKind.absoluteValue"/>
</cim:OperationalLimitType>
<cim:EnergyConsumer rdf:ID="Load7-I">
<cim:IdentifiedObject.name>Load7-I</cim:IdentifiedObject.name>
......@@ -913,7 +913,7 @@
<cim:TransformerEnd.grounded>True</cim:TransformerEnd.grounded>
<cim:PowerTransformerEnd.PowerTransformer rdf:resource="#TR1"/>
<cim:PowerTransformerEnd.b>-1.9835e-06</cim:PowerTransformerEnd.b>
<cim:PowerTransformerEnd.connectionKind>Y</cim:PowerTransformerEnd.connectionKind>
<cim:PowerTransformerEnd.connectionKind rdf:resource="http://iec.ch/TC57/2012/CIM-schema-cim16#WindingConnection.Y"/>
<cim:PowerTransformerEnd.ratedS>40.0</cim:PowerTransformerEnd.ratedS>
<cim:PowerTransformerEnd.ratedU>110.0</cim:PowerTransformerEnd.ratedU>
<cim:PowerTransformerEnd.r>1.90575</cim:PowerTransformerEnd.r>
......@@ -953,7 +953,7 @@
<cim:TransformerEnd.endNumber>2</cim:TransformerEnd.endNumber>
<cim:TransformerEnd.grounded>True</cim:TransformerEnd.grounded>
<cim:PowerTransformerEnd.PowerTransformer rdf:resource="#TR1"/>
<cim:PowerTransformerEnd.connectionKind>D</cim:PowerTransformerEnd.connectionKind>
<cim:PowerTransformerEnd.connectionKind rdf:resource="http://iec.ch/TC57/2012/CIM-schema-cim16#WindingConnection.D"/>
<cim:PowerTransformerEnd.ratedS>40.0</cim:PowerTransformerEnd.ratedS>
<cim:PowerTransformerEnd.ratedU>20.0</cim:PowerTransformerEnd.ratedU>
<cim:PowerTransformerEnd.phaseAngleClock>5</cim:PowerTransformerEnd.phaseAngleClock>
......@@ -999,7 +999,7 @@
<cim:TransformerEnd.grounded>True</cim:TransformerEnd.grounded>
<cim:PowerTransformerEnd.PowerTransformer rdf:resource="#TR2"/>
<cim:PowerTransformerEnd.b>-1.9835e-06</cim:PowerTransformerEnd.b>
<cim:PowerTransformerEnd.connectionKind>Y</cim:PowerTransformerEnd.connectionKind>
<cim:PowerTransformerEnd.connectionKind rdf:resource="http://iec.ch/TC57/2012/CIM-schema-cim16#WindingConnection.Y"/>
<cim:PowerTransformerEnd.ratedS>40.0</cim:PowerTransformerEnd.ratedS>
<cim:PowerTransformerEnd.ratedU>110.0</cim:PowerTransformerEnd.ratedU>
<cim:PowerTransformerEnd.r>1.90575</cim:PowerTransformerEnd.r>
......@@ -1039,7 +1039,7 @@
<cim:TransformerEnd.endNumber>2</cim:TransformerEnd.endNumber>
<cim:TransformerEnd.grounded>True</cim:TransformerEnd.grounded>
<cim:PowerTransformerEnd.PowerTransformer rdf:resource="#TR2"/>
<cim:PowerTransformerEnd.connectionKind>D</cim:PowerTransformerEnd.connectionKind>
<cim:PowerTransformerEnd.connectionKind rdf:resource="http://iec.ch/TC57/2012/CIM-schema-cim16#WindingConnection.D"/>
<cim:PowerTransformerEnd.ratedS>40.0</cim:PowerTransformerEnd.ratedS>
<cim:PowerTransformerEnd.ratedU>20.0</cim:PowerTransformerEnd.ratedU>
<cim:PowerTransformerEnd.phaseAngleClock>5</cim:PowerTransformerEnd.phaseAngleClock>
......
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cim="http://iec.ch/TC57/2012/CIM-schema-cim16#" xmlns:md="http://iec.ch/TC57/61970-552/ModelDescription/1#" xmlns:entsoe="http://entsoe.eu/Secretariat/ProfileExtension/2#" xmlns:neplan="http://www.neplan.ch#" >
<md:FullModel rdf:about="Test">
<md:Model.created>27/01/2020 18:45:27</md:Model.created>
<md:FullModel rdf:about="CIGREMV_reference_cgmes_v2_4_15">
<md:Model.created>29/01/2020 15:09:48</md:Model.created>
<md:Model.modelingAuthoritySet>www.acs.eonerc.rwth-aachen.de</md:Model.modelingAuthoritySet>
<md:Model.profile>StateVariables</md:Model.profile>
</md:FullModel>
......
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cim="http://iec.ch/TC57/2012/CIM-schema-cim16#" xmlns:md="http://iec.ch/TC57/61970-552/ModelDescription/1#" xmlns:entsoe="http://entsoe.eu/Secretariat/ProfileExtension/2#" xmlns:neplan="http://www.neplan.ch#" >
<md:FullModel rdf:about="Test">
<md:Model.created>27/01/2020 18:45:27</md:Model.created>
<md:FullModel rdf:about="CIGREMV_reference_cgmes_v2_4_15">
<md:Model.created>29/01/2020 15:09:48</md:Model.created>
<md:Model.modelingAuthoritySet>www.acs.eonerc.rwth-aachen.de</md:Model.modelingAuthoritySet>
<md:Model.profile>Topology</md:Model.profile>
</md:FullModel>
......
......@@ -23,8 +23,8 @@ example_path = os.path.join('..',
'CIGRE_MV_Rudion_With_LoadFlow_Results'))))
# test export with imported files
# test cimpy export with exported files
# This test tests the export functionality of this package by first importing the CIGRE_MV_Rudion_With_LoadFlow_Results
# example and exporting them. The exported files are compared with previously exported files which were checked manually
def test_export_with_exported_files():
import_files = [os.path.join(example_path, 'Rootnet_FULL_NE_24J13h_DI.xml'),
os.path.join(example_path, 'Rootnet_FULL_NE_24J13h_EQ.xml'),
......@@ -33,8 +33,9 @@ def test_export_with_exported_files():
activeProfileList = ['DI', 'EQ', 'SV', 'TP']
imported_files, namespaces = cimpy.cim_import(import_files, 'cgmes_v2_4_15')
cimpy.cim_export(imported_files, namespaces, 'EXPORTED_Test', 'cgmes_v2_4_15', activeProfileList)
imported_files, namespaces, url_reference_dict = cimpy.cim_import(import_files, 'cgmes_v2_4_15')
cimpy.cim_export(imported_files, namespaces, 'EXPORTED_Test', 'cgmes_v2_4_15',
activeProfileList, url_reference_dict)
test_list = []
for file in os.listdir(os.getcwd()):
......
......@@ -23,6 +23,9 @@ example_path = os.path.join('..',
'CIGRE_MV_Rudion_With_LoadFlow_Results'))))
# This test function tests the export functionality by comparing files before the import and export procedure with the
# exported files. Since cyclic attributes are not resolved in this package, the imported files only need to be a subset
# of the exported files.
def test_export_with_imported_files():
import_files = [os.path.join(example_path, 'Rootnet_FULL_NE_24J13h_DI.xml'),
os.path.join(example_path, 'Rootnet_FULL_NE_24J13h_EQ.xml'),
......@@ -31,8 +34,9 @@ def test_export_with_imported_files():
activeProfileList = ['DI', 'EQ', 'SV', 'TP']
imported_files, namespaces = cimpy.cim_import(import_files, 'cgmes_v2_4_15')
cimpy.cim_export(imported_files, namespaces, 'EXPORTED_Test', 'cgmes_v2_4_15', activeProfileList)
imported_files, namespaces, url_reference_dict = cimpy.cim_import(import_files, 'cgmes_v2_4_15')
cimpy.cim_export(imported_files, namespaces, 'EXPORTED_Test', 'cgmes_v2_4_15',
activeProfileList, url_reference_dict)
test_list = []
for file in import_files:
......@@ -131,8 +135,7 @@ def test_export_with_imported_files():
export_attr = export_class_dict[mRID].items()
for item in test_attr:
if item[0] in ['cim:NameType', 'cim:ExternalNetworkInjection.referencePriority',
'cim:Terminal.connected', 'cim:PowerTransformerEnd.connectionKind',
'cim:OperationalLimitType.direction', 'cim:Diagram.orientation']:
'cim:Terminal.connected']:
continue
elif item[0] == 'cim:Terminal.sequenceNumber':
test_item = 'cim:ACDCTerminal.sequenceNumber'
......
......@@ -23,15 +23,18 @@ example_path = os.path.join('..',
'CIGRE_MV_Rudion_With_LoadFlow_Results'))))
# This function tests the import functionality by importing files and comparing them to previously imported and pickled
# files.
def test_import():
test_files = [os.path.join(example_path, 'Rootnet_FULL_NE_24J13h_DI.xml'),
os.path.join(example_path, 'Rootnet_FULL_NE_24J13h_EQ.xml'),
os.path.join(example_path, 'Rootnet_FULL_NE_24J13h_SV.xml'),
os.path.join(example_path, 'Rootnet_FULL_NE_24J13h_TP.xml'), ]
imported_files, _ = cimpy.cim_import(test_files, 'cgmes_v2_4_15')
imported_files, _, url_reference_dict = cimpy.cim_import(test_files, 'cgmes_v2_4_15')
import_resolved = cimpy.cimexport._get_class_attributes_with_references(imported_files, 'cgmes_v2_4_15')
import_resolved = cimpy.cimexport._get_class_attributes_with_references(imported_files,
'cgmes_v2_4_15', url_reference_dict)
check_dict_pickle = pickle.load(open('CIGREMV_import_reference_cgmes_v2_4_15.p', 'rb'))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment