diff --git a/gridappsd-field-bus-lib/gridappsd_field_bus/field_interface/field_proxy_forwarder.py b/gridappsd-field-bus-lib/gridappsd_field_bus/field_interface/field_proxy_forwarder.py index b575efc..8722ac4 100644 --- a/gridappsd-field-bus-lib/gridappsd_field_bus/field_interface/field_proxy_forwarder.py +++ b/gridappsd-field-bus-lib/gridappsd_field_bus/field_interface/field_proxy_forwarder.py @@ -14,8 +14,8 @@ from cimgraph.databases import BlazegraphConnection from cimgraph.models import BusBranchModel +import importlib import os -import cimgraph.data_profile.cimhub_ufls as cim REQUEST_FIELD = ".".join((topics.PROCESS_PREFIX, "request.field")) @@ -64,7 +64,16 @@ class FieldProxyForwarder: when direct connection is not possible. """ - def __init__(self, connection_url: str, username: str, password: str, mrid: str): + def __init__( + self, + connection_url: str, + username: str, + password: str, + mrid: str, + cim_profile: str = os.environ.get("CIMG_CIM_PROFILE", "cimhub_2023"), + ): + self.cim = importlib.import_module("cimgraph.data_profile." + cim_profile) + # Connect to OT self.ot_connection = GridAPPSD() @@ -89,7 +98,7 @@ def __init__(self, connection_url: str, username: str, password: str, mrid: str) # Subscribe to messages on OT bus self.ot_connection.subscribe(topics.field_input_topic(), self.on_message_from_ot) - os.environ["CIMG_CIM_PROFILE"] = "cimhub_ufls" + os.environ["CIMG_CIM_PROFILE"] = cim_profile os.environ["CIMG_URL"] = "http://localhost:8889/bigdata/namespace/kb/sparql" os.environ["CIMG_DATABASE"] = "powergridmodel" os.environ["CIMG_NAMESPACE"] = "http://iec.ch/TC57/CIM100#" @@ -97,12 +106,12 @@ def __init__(self, connection_url: str, username: str, password: str, mrid: str) os.environ["CIMG_USE_UNITS"] = "False" self.database = BlazegraphConnection() - distribution_area = cim.DistributionArea(mRID=mrid) + distribution_area = self.cim.DistributionArea(mRID=mrid) self.network = BusBranchModel(connection=self.database, container=distribution_area, distributed=False) - self.network.get_all_edges(cim.DistributionArea) - self.network.get_all_edges(cim.Substation) + self.network.get_all_edges(self.cim.DistributionArea) + self.network.get_all_edges(self.cim.Substation) - for substation in self.network.graph.get(cim.Substation, {}).values(): + for substation in self.network.graph.get(self.cim.Substation, {}).values(): mrid = substation.mRID # type: ignore[attr-defined] print(f"Subscribing to Substation: /topic/goss.gridappsd.field.{mrid}") self.ot_connection.subscribe("/topic/goss.gridappsd.field." + mrid, self.on_message_from_ot) diff --git a/gridappsd-field-bus-lib/gridappsd_field_bus/forwarder.py b/gridappsd-field-bus-lib/gridappsd_field_bus/forwarder.py index 66ed250..a650093 100644 --- a/gridappsd-field-bus-lib/gridappsd_field_bus/forwarder.py +++ b/gridappsd-field-bus-lib/gridappsd_field_bus/forwarder.py @@ -33,13 +33,21 @@ show_default="from environment variable GRIDAPPSD_ADDRESS", help="Connection URL.", ) -def start_forwarder(username, password, connection_url): +@click.option( + "--mrid", + default=lambda: os.getenv("GRIDAPPSD_FIELD_BUS_MRID"), + type=str, + metavar="MRID", + show_default="from environment variable GRIDAPPSD_FIELD_BUS_MRID", + help="mRID of the distribution area/substation to bridge for field bus forwarding.", +) +def start_forwarder(username, password, connection_url, mrid): """Start the field proxy forwarder with either a YAML configuration or cmd-line arguments.""" - required = [username, password, connection_url] + required = [username, password, connection_url, mrid] if not all(required): click.echo( - "Username, password, and connection URL must be provided either through environment variables or command-line arguments." + "Username, password, connection URL, and mrid must be provided either through environment variables or command-line arguments." ) click.Abort() @@ -49,9 +57,14 @@ def start_forwarder(username, password, connection_url): click.Abort() # Use command-line arguments - click.echo(f"Using command line arguments: {username}, {password}, {connection_url}") + click.echo(f"Using command line arguments: {username}, {password}, {connection_url}, {mrid}") - FieldProxyForwarder(username, password, connection_url, None) + FieldProxyForwarder( + connection_url=connection_url, + username=username, + password=password, + mrid=mrid, + ) time.sleep(0.1) diff --git a/gridappsd-field-bus-lib/tests/test_field_proxy_forwarder_cim_profile.py b/gridappsd-field-bus-lib/tests/test_field_proxy_forwarder_cim_profile.py new file mode 100644 index 0000000..c3c1533 --- /dev/null +++ b/gridappsd-field-bus-lib/tests/test_field_proxy_forwarder_cim_profile.py @@ -0,0 +1,20 @@ +import importlib + +import pytest + + +@pytest.mark.parametrize("cim_profile", ["cimhub_2023"]) +def test_field_proxy_forwarder_imports_cleanly_and_profile_has_required_types(cim_profile): + """FieldProxyForwarder must import without raising ModuleNotFoundError, and + the configured CIM profile module must expose DistributionArea and + Substation, since FieldProxyForwarder.__init__ resolves both from it. + """ + # Import the module under test. This must not raise ModuleNotFoundError, + # which was the failure mode when the module imported the now-removed + # cimhub_ufls profile unconditionally at module scope. + from gridappsd_field_bus.field_interface import field_proxy_forwarder # noqa: F401 + + profile_module = importlib.import_module("cimgraph.data_profile." + cim_profile) + + assert hasattr(profile_module, "DistributionArea") + assert hasattr(profile_module, "Substation")