Skip to content
Merged
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
* None.

### New Features
* None.
* Added relationships between `Asset` and `PowerSystemResource` which enables linking `Equipment` to `Pole`:
* `Asset.powerSystemResources`
* `PowerSystemResource.assets`

### Enhancements
* None.
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

deps = [
"zepben.auth==0.12.1",
"zepben.protobuf==0.34.1",
"zepben.protobuf==0.35.0",
"typing_extensions==4.12.2",
]

Expand All @@ -19,7 +19,7 @@
"pytest-asyncio==0.19.0",
"pytest-timeout==1.4.2",
"hypothesis==6.56.3",
"grpcio-testing==1.57.0",
"grpcio-testing==1.71.0",
"pylint==2.14.5",
"six==1.16.0",
"tox"
Expand Down
3 changes: 3 additions & 0 deletions src/zepben/evolve/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@
from zepben.evolve.database.sqlite.tables.table_version import *
from zepben.evolve.database.sqlite.tables.associations.loop_substation_relationship import *
from zepben.evolve.database.sqlite.tables.associations.table_asset_organisation_roles_assets import *
from zepben.evolve.database.sqlite.tables.associations.table_assets_power_system_resources import *
from zepben.evolve.database.sqlite.tables.associations.table_battery_units_battery_controls import *
from zepben.evolve.database.sqlite.tables.associations.table_end_devices_end_device_functions import *
from zepben.evolve.database.sqlite.tables.associations.table_circuits_substations import *
Expand All @@ -266,6 +267,7 @@
from zepben.evolve.database.sqlite.tables.associations.table_protection_relay_functions_protected_switches import *
from zepben.evolve.database.sqlite.tables.associations.table_protection_relay_functions_sensors import *
from zepben.evolve.database.sqlite.tables.associations.table_protection_relay_schemes_protection_relay_functions import *
from zepben.evolve.database.sqlite.tables.associations.table_synchronous_machines_reactive_capability_curves import *
from zepben.evolve.database.sqlite.tables.associations.table_usage_points_end_devices import *
from zepben.evolve.database.sqlite.tables.iec61968.assetinfo.table_cable_info import *
from zepben.evolve.database.sqlite.tables.iec61968.assetinfo.table_no_load_tests import *
Expand Down Expand Up @@ -320,6 +322,7 @@
from zepben.evolve.database.sqlite.tables.iec61970.base.core.table_conducting_equipment import *
from zepben.evolve.database.sqlite.tables.iec61970.base.core.table_connectivity_node_containers import *
from zepben.evolve.database.sqlite.tables.iec61970.base.core.table_connectivity_nodes import *
from zepben.evolve.database.sqlite.tables.iec61970.base.core.table_curve_data import *
from zepben.evolve.database.sqlite.tables.iec61970.base.core.table_equipment import *
from zepben.evolve.database.sqlite.tables.iec61970.base.core.table_equipment_containers import *
from zepben.evolve.database.sqlite.tables.iec61970.base.core.table_feeders import *
Expand Down
31 changes: 31 additions & 0 deletions src/zepben/evolve/database/sqlite/network/network_cim_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
from zepben.evolve.database.sqlite.extensions.result_set import ResultSet
from zepben.evolve.database.sqlite.tables.associations.loop_substation_relationship import LoopSubstationRelationship
from zepben.evolve.database.sqlite.tables.associations.table_asset_organisation_roles_assets import TableAssetOrganisationRolesAssets
from zepben.evolve.database.sqlite.tables.associations.table_assets_power_system_resources import TableAssetsPowerSystemResources
from zepben.evolve.database.sqlite.tables.associations.table_circuits_substations import TableCircuitsSubstations
from zepben.evolve.database.sqlite.tables.associations.table_circuits_terminals import TableCircuitsTerminals
from zepben.evolve.database.sqlite.tables.associations.table_equipment_equipment_containers import TableEquipmentEquipmentContainers
Expand Down Expand Up @@ -2718,6 +2719,36 @@ def load_asset_organisation_roles_asset(

return True

def load_asset_power_system_resources(
self,
table: TableAssetsPowerSystemResources,
result_set: ResultSet,
set_identifier: Callable[[str], str]
) -> bool:
"""
Create a :class:`Asset` to :class:`PowerSystemResource` association from :class:`TableAssetPowerSystemResources`.

:param table: The database table to read the association from.
:param result_set: The record in the database table containing the fields for this association.
:param set_identifier: A callback to register the identifier of this association for logging purposes.

:return: True if the association was successfully read from the database and added to the service.
:raises SqlException: For any errors encountered reading from the database.
"""
asset_mrid = result_set.get_string(table.asset_mrid.query_index)
set_identifier(f"{asset_mrid}-to-UNKNOWN")

power_system_resource_mrid = result_set.get_string(table.power_system_resource_mrid.query_index)
set_identifier(f"{asset_mrid}-to-{power_system_resource_mrid}")

asset = self._service.get(asset_mrid, Asset)
power_system_resource = self._service.get(power_system_resource_mrid, PowerSystemResource)

asset.add_power_system_resource(power_system_resource)
power_system_resource.add_asset(asset)

return True

def load_battery_units_battery_controls(self, table: TableBatteryUnitsBatteryControls, result_set: ResultSet, set_identifier: Callable[[str], str]) -> bool:
"""
Create a :class:`BatteryUnit` to :class:`BatteryControl` association from :class:`TableBatteryUnitsBatteryControls`.
Expand Down
13 changes: 12 additions & 1 deletion src/zepben/evolve/database/sqlite/network/network_cim_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from zepben.evolve.database.sqlite.network.network_database_tables import NetworkDatabaseTables
from zepben.evolve.database.sqlite.tables.associations.loop_substation_relationship import LoopSubstationRelationship
from zepben.evolve.database.sqlite.tables.associations.table_asset_organisation_roles_assets import TableAssetOrganisationRolesAssets
from zepben.evolve.database.sqlite.tables.associations.table_assets_power_system_resources import TableAssetsPowerSystemResources
from zepben.evolve.database.sqlite.tables.associations.table_battery_units_battery_controls import TableBatteryUnitsBatteryControls
from zepben.evolve.database.sqlite.tables.associations.table_circuits_substations import TableCircuitsSubstations
from zepben.evolve.database.sqlite.tables.associations.table_circuits_terminals import TableCircuitsTerminals
Expand Down Expand Up @@ -518,6 +519,8 @@ def _save_asset(self, table: TableAssets, insert: PreparedStatement, asset: Asse
insert.add_value(table.location_mrid.query_index, self._mrid_or_none(asset.location))
for it in asset.organisation_roles:
status = status and self._save_asset_organisation_role_to_asset_association(it, asset)
for it in asset.power_system_resources:
status = status and self._save_asset_to_power_system_resource_association(it, asset)

return status and self._save_identified_object(table, insert, asset, description)

Expand Down Expand Up @@ -2357,6 +2360,15 @@ def _save_asset_organisation_role_to_asset_association(self, asset_organisation_

return self._try_execute_single_update(insert, "asset organisation role to asset association")

def _save_asset_to_power_system_resource_association(self, power_system_resource: PowerSystemResource, asset: Asset) -> bool:
table = self._database_tables.get_table(TableAssetsPowerSystemResources)
insert = self._database_tables.get_insert(TableAssetsPowerSystemResources)

insert.add_value(table.asset_mrid.query_index, asset.mrid)
insert.add_value(table.power_system_resource_mrid.query_index, power_system_resource.mrid)

return self._try_execute_single_update(insert, "asset to power system resource association")

def _save_battery_unit_to_battery_control_association(self, battery_unit: BatteryUnit, battery_control: BatteryControl) -> bool:
table = self._database_tables.get_table(TableBatteryUnitsBatteryControls)
insert = self._database_tables.get_insert(TableBatteryUnitsBatteryControls)
Expand Down Expand Up @@ -2393,7 +2405,6 @@ def _save_end_device_function_to_end_device_association(self, end_device_functio

return self._try_execute_single_update(insert, "end device function to end device association")


def _save_equipment_to_equipment_container_association(self, equipment: Equipment, equipment_container: EquipmentContainer) -> bool:
table = self._database_tables.get_table(TableEquipmentEquipmentContainers)
insert = self._database_tables.get_insert(TableEquipmentEquipmentContainers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from zepben.evolve.database.sqlite.common.base_database_tables import BaseDatabaseTables
from zepben.evolve.database.sqlite.tables.associations.table_asset_organisation_roles_assets import *
from zepben.evolve.database.sqlite.tables.associations.table_assets_power_system_resources import TableAssetsPowerSystemResources
from zepben.evolve.database.sqlite.tables.associations.table_battery_units_battery_controls import *
from zepben.evolve.database.sqlite.tables.associations.table_circuits_substations import *
from zepben.evolve.database.sqlite.tables.associations.table_circuits_terminals import *
Expand Down Expand Up @@ -133,6 +134,7 @@ def _included_tables(self) -> Generator[SqliteTable, None, None]:
yield TableAccumulators()
yield TableAnalogs()
yield TableAssetOrganisationRolesAssets()
yield TableAssetsPowerSystemResources()
yield TableAssetOwners()
yield TableBaseVoltages()
yield TableBatteryControls()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from zepben.evolve.database.sqlite.network.network_cim_reader import NetworkCimReader
from zepben.evolve.database.sqlite.network.network_database_tables import NetworkDatabaseTables
from zepben.evolve.database.sqlite.tables.associations.table_asset_organisation_roles_assets import TableAssetOrganisationRolesAssets
from zepben.evolve.database.sqlite.tables.associations.table_assets_power_system_resources import TableAssetsPowerSystemResources
from zepben.evolve.database.sqlite.tables.associations.table_battery_units_battery_controls import TableBatteryUnitsBatteryControls
from zepben.evolve.database.sqlite.tables.associations.table_circuits_substations import TableCircuitsSubstations
from zepben.evolve.database.sqlite.tables.associations.table_circuits_terminals import TableCircuitsTerminals
Expand Down Expand Up @@ -241,6 +242,7 @@ def _do_load(self) -> bool:
self._load_each(TablePositionPoints, self._reader.load_position_point),
self._load_each(TableLocationStreetAddresses, self._reader.load_location_street_address),
self._load_each(TableAssetOrganisationRolesAssets, self._reader.load_asset_organisation_roles_asset),
self._load_each(TableAssetsPowerSystemResources, self._reader.load_asset_power_system_resources),
self._load_each(TableUsagePointsEndDevices, self._reader.load_usage_points_end_device),
self._load_each(TableEquipmentUsagePoints, self._reader.load_equipment_usage_point),
self._load_each(TableEquipmentOperationalRestrictions, self._reader.load_equipment_operational_restriction),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright 2025 Zeppelin Bend Pty Ltd
# 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 https://mozilla.org/MPL/2.0/.

from typing import List, Generator

from zepben.evolve.database.sqlite.tables.column import Column, Nullable
from zepben.evolve.database.sqlite.tables.sqlite_table import SqliteTable

__all__ = ["TableAssetsPowerSystemResources"]


class TableAssetsPowerSystemResources(SqliteTable):
"""
A class representing the association between AssetOrganisationRoles and Assets.
"""

def __init__(self):
super().__init__()

self.asset_mrid: Column = self._create_column("asset_mrid", "TEXT", Nullable.NOT_NULL)
"""A column storing the mRID of Assets."""

self.power_system_resource_mrid: Column = self._create_column("power_system_resource_mrid", "TEXT", Nullable.NOT_NULL)
"""A column storing the mRID of PowerSystemResources."""

@property
def name(self) -> str:
return "assets_power_system_resources"

@property
def unique_index_columns(self) -> Generator[List[Column], None, None]:
yield from super().unique_index_columns
yield [self.asset_mrid, self.power_system_resource_mrid]

@property
def non_unique_index_columns(self) -> Generator[List[Column], None, None]:
yield from super().non_unique_index_columns
yield [self.asset_mrid]
yield [self.power_system_resource_mrid]
2 changes: 1 addition & 1 deletion src/zepben/evolve/database/sqlite/tables/table_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@


class TableVersion(SqliteTable):
SUPPORTED_VERSION = 58
SUPPORTED_VERSION = 59

def __init__(self):
self.version: Column = self._create_column("version", "TEXT", Nullable.NOT_NULL)
Expand Down
70 changes: 65 additions & 5 deletions src/zepben/evolve/model/cim/iec61968/assets/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

if TYPE_CHECKING:
from zepben.evolve import AssetOrganisationRole
from zepben.evolve import PowerSystemResource

from zepben.evolve.model.cim.iec61968.common.location import Location
from zepben.evolve.model.cim.iec61970.base.core.identified_object import IdentifiedObject
Expand All @@ -30,12 +31,18 @@ class Asset(IdentifiedObject):

_organisation_roles: Optional[List[AssetOrganisationRole]] = None

def __init__(self, organisation_roles: List[AssetOrganisationRole] = None, **kwargs):
_power_system_resources: Optional[List[PowerSystemResource]] = None

def __init__(self, organisation_roles: List[AssetOrganisationRole] = None, power_system_resources: List[PowerSystemResource] = None, **kwargs):
super(Asset, self).__init__(**kwargs)
if organisation_roles:
for role in organisation_roles:
self.add_organisation_role(role)

if power_system_resources:
for resource in power_system_resources:
self.add_power_system_resource(resource)

def num_organisation_roles(self) -> int:
"""
Get the number of `AssetOrganisationRole`s associated with this `Asset`.
Expand All @@ -61,8 +68,7 @@ def get_organisation_role(self, mrid: str) -> AssetOrganisationRole:

def add_organisation_role(self, role: AssetOrganisationRole) -> Asset:
"""
`role` The `AssetOrganisationRole` to
associate with this `Asset`.
`role` The `AssetOrganisationRole` to associate with this `Asset`.
Returns A reference to this `Asset` to allow fluent use.
Raises `ValueError` if another `AssetOrganisationRole` with the same `mrid` already exists in this `Asset`
"""
Expand All @@ -77,8 +83,7 @@ def remove_organisation_role(self, role: AssetOrganisationRole) -> Asset:
"""
Disassociate an `AssetOrganisationRole` from this `Asset`.

`role` the `AssetOrganisationRole` to
disassociate with this `Asset`.
`role` the `AssetOrganisationRole` to disassociate from this `Asset`.
Raises `ValueError` if `role` was not associated with this `Asset`.
Returns A reference to this `Asset` to allow fluent use.
"""
Expand All @@ -93,6 +98,61 @@ def clear_organisation_roles(self) -> Asset:
self._organisation_roles = None
return self

def num_power_system_resources(self) -> int:
"""
Get the number of `PowerSystemResource`s associated with this `Asset`.
"""
return nlen(self._power_system_resources)

@property
def power_system_resources(self) -> Generator[PowerSystemResource, None, None]:
"""
The `PowerSystemResource`s of this `Asset`.
"""
return ngen(self._power_system_resources)

def get_power_system_resource(self, mrid: str) -> PowerSystemResource:
"""
Get the `PowerSystemResource` for this asset identified by `mrid`.

`mrid` the mRID of the required `PowerSystemResource`
Returns The `PowerSystemResource` with the specified `mrid`.
Raises `KeyError` if `mrid` wasn't present.
"""
return get_by_mrid(self._power_system_resources, mrid)

def add_power_system_resource(self, resource: PowerSystemResource) -> Asset:
"""
`resource` The `PowerSystemResource` to associate with this `Asset`.
Returns A reference to this `Asset` to allow fluent use.
Raises `ValueError` if another `PowerSystemResource` with the same `mrid` already exists in this `Asset`
"""
if self._validate_reference(resource, self.get_power_system_resource, "An PowerSystemResource"):
return self

self._power_system_resources = list() if self._power_system_resources is None else self._power_system_resources
self._power_system_resources.append(resource)
return self

def remove_power_system_resource(self, resource: PowerSystemResource) -> Asset:
"""
Disassociate an `PowerSystemResource` from this `Asset`.

`resource` the `PowerSystemResource` to disassociate from this `Asset`.
Raises `ValueError` if `resource` was not associated with this `Asset`.
Returns A reference to this `Asset` to allow fluent use.
"""
self._power_system_resources = safe_remove(self._power_system_resources, resource)
return self

def clear_power_system_resources(self) -> Asset:
"""
Clear all power system resources.
Returns self
"""
self._power_system_resources = None
return self


class AssetContainer(Asset):
"""
Expand Down
Loading