From 0b37a38ab056b592bbe5b4060d024407cc516d6e Mon Sep 17 00:00:00 2001 From: Martin Skorupski Date: Thu, 27 Mar 2025 12:13:49 +0100 Subject: [PATCH] OAM-447 Align Cell generation in ietf-topo with geo.json - 3 smaller topos added to config - convert NRCellDUs to TerminationPoints Issue-ID: OAM-447 Change-Id: I247a91c6b3d6a5d75646e032e9ed63c052afe01c Signed-off-by: Martin Skorupski --- .../configurations/configA00011.json | 80 ++++++++++++++++++++++ .../configurations/configB00011.json | 80 ++++++++++++++++++++++ .../configurations/configC00011.json | 80 ++++++++++++++++++++++ .../network_generation/model/python/nr_cell_du.py | 17 ++++- .../network_generation/model/python/o_ran_ru.py | 64 +++++++++++++++-- 5 files changed, 311 insertions(+), 10 deletions(-) create mode 100644 code/network-generator/configurations/configA00011.json create mode 100644 code/network-generator/configurations/configB00011.json create mode 100644 code/network-generator/configurations/configC00011.json diff --git a/code/network-generator/configurations/configA00011.json b/code/network-generator/configurations/configA00011.json new file mode 100644 index 0000000..e7a7743 --- /dev/null +++ b/code/network-generator/configurations/configA00011.json @@ -0,0 +1,80 @@ +{ + "network": { + "name": "O-RAN-Network-Operator-A-small", + "host": "to-be-replaced-by-host-fqdn", + "version":"v0.0.2", + "operationalState": "enabled", + "center": { + "latitude": 40.545, + "longitude": -74.447, + "aboveMeanSeaLevel": 51 + }, + "disabledResourcesProfile": { + "o-ran-sc-network:smo": 0, + "o-ran-sc-network:o-cloud": 0, + "o-ran-sc-network:tower": 0, + "o-ran-sc-network:near-rt-ric": 0, + "o-ran-common-identity-refs:o-cu-function": 0, + "o-ran-common-identity-refs:o-du-function": 0, + "o-ran-common-identity-refs:o-ru-function": 15, + "o-ran-sc-network:cell": 0 + }, + "pattern": { + "tower": { + "representation": "hexagon" + }, + "smo": { + "fiveGCoreCount": 1, + "nearRtRicSpiralRadius": 0, + "oCloudResourcePoolCount": 1 + }, + "nearRtRic": { + "oRanCuSpiralRadius":0 + }, + "oRanCu": { + "oRanDuSpiralRadius": 0 + }, + "oRanDu": { + "fronthaulGatewayCount": 0, + "towerSpiralRadius": 1, + "oRanRuCount": 1 + }, + "oRanRu": { + "nrCellDuCount": 1 + }, + "nrCellDu": { + "sectorCount": 1, + "cellAngle": 120, + "cellScaleFactorForHandoverArea": 20, + "maxReach": 400 + } + } + }, + "outputFolder": "output", + "generationTasks": { + "day0Config": { + "enabled": false, + "compressed": false + }, + "rfc8345": { + "enabled": true, + "compressed": false + }, + "svg": { + "enabled": false, + "compressed": false + }, + "kml": { + "enabled": true, + "compressed": true + }, + "rfc7946": { + "enabled": true, + "compressed": false + }, + "teiv": { + "enabled": false, + "compressed": false + } + } +} \ No newline at end of file diff --git a/code/network-generator/configurations/configB00011.json b/code/network-generator/configurations/configB00011.json new file mode 100644 index 0000000..1257e75 --- /dev/null +++ b/code/network-generator/configurations/configB00011.json @@ -0,0 +1,80 @@ +{ + "network": { + "name": "O-RAN-Network-Operator-B-small", + "host": "to-be-replaced-by-host-fqdn", + "version":"v0.0.2", + "operationalState": "enabled", + "center": { + "latitude": 40.535, + "longitude": -74.457, + "aboveMeanSeaLevel": 52 + }, + "disabledResourcesProfile": { + "o-ran-sc-network:smo": 0, + "o-ran-sc-network:o-cloud": 0, + "o-ran-sc-network:tower": 0, + "o-ran-sc-network:near-rt-ric": 0, + "o-ran-common-identity-refs:o-cu-function": 0, + "o-ran-common-identity-refs:o-du-function": 0, + "o-ran-common-identity-refs:o-ru-function": 25, + "o-ran-sc-network:cell": 0 + }, + "pattern": { + "tower": { + "representation": "hexagon" + }, + "smo": { + "fiveGCoreCount": 1, + "nearRtRicSpiralRadius": 0, + "oCloudResourcePoolCount": 1 + }, + "nearRtRic": { + "oRanCuSpiralRadius":0 + }, + "oRanCu": { + "oRanDuSpiralRadius": 0 + }, + "oRanDu": { + "fronthaulGatewayCount": 0, + "towerSpiralRadius": 1, + "oRanRuCount": 1 + }, + "oRanRu": { + "nrCellDuCount": 1 + }, + "nrCellDu": { + "sectorCount": 1, + "cellAngle": 120, + "cellScaleFactorForHandoverArea": 20, + "maxReach": 400 + } + } + }, + "outputFolder": "output", + "generationTasks": { + "day0Config": { + "enabled": false, + "compressed": false + }, + "rfc8345": { + "enabled": true, + "compressed": false + }, + "svg": { + "enabled": false, + "compressed": false + }, + "kml": { + "enabled": true, + "compressed": true + }, + "rfc7946": { + "enabled": true, + "compressed": false + }, + "teiv": { + "enabled": false, + "compressed": false + } + } +} \ No newline at end of file diff --git a/code/network-generator/configurations/configC00011.json b/code/network-generator/configurations/configC00011.json new file mode 100644 index 0000000..d65bb41 --- /dev/null +++ b/code/network-generator/configurations/configC00011.json @@ -0,0 +1,80 @@ +{ + "network": { + "name": "O-RAN-Network-Operator-C-small", + "host": "to-be-replaced-by-host-fqdn", + "version":"v0.0.2", + "operationalState": "enabled", + "center": { + "latitude": 40.535, + "longitude": -74.447, + "aboveMeanSeaLevel": 53 + }, + "disabledResourcesProfile": { + "o-ran-sc-network:smo": 0, + "o-ran-sc-network:o-cloud": 0, + "o-ran-sc-network:tower": 0, + "o-ran-sc-network:near-rt-ric": 0, + "o-ran-common-identity-refs:o-cu-function": 0, + "o-ran-common-identity-refs:o-du-function": 0, + "o-ran-common-identity-refs:o-ru-function": 20, + "o-ran-sc-network:cell": 0 + }, + "pattern": { + "tower": { + "representation": "hexagon" + }, + "smo": { + "fiveGCoreCount": 1, + "nearRtRicSpiralRadius": 0, + "oCloudResourcePoolCount": 1 + }, + "nearRtRic": { + "oRanCuSpiralRadius": 0 + }, + "oRanCu": { + "oRanDuSpiralRadius": 0 + }, + "oRanDu": { + "fronthaulGatewayCount": 0, + "towerSpiralRadius": 1, + "oRanRuCount": 1 + }, + "oRanRu": { + "nrCellDuCount": 1 + }, + "nrCellDu": { + "sectorCount": 1, + "cellAngle": 120, + "cellScaleFactorForHandoverArea": 20, + "maxReach": 400 + } + } + }, + "outputFolder": "output", + "generationTasks": { + "day0Config": { + "enabled": false, + "compressed": false + }, + "rfc8345": { + "enabled": true, + "compressed": false + }, + "svg": { + "enabled": false, + "compressed": false + }, + "kml": { + "enabled": true, + "compressed": true + }, + "rfc7946": { + "enabled": true, + "compressed": false + }, + "teiv": { + "enabled": false, + "compressed": false + } + } +} \ No newline at end of file diff --git a/code/network-generator/network_generation/model/python/nr_cell_du.py b/code/network-generator/network_generation/model/python/nr_cell_du.py index fc7900e..01c866e 100644 --- a/code/network-generator/network_generation/model/python/nr_cell_du.py +++ b/code/network-generator/network_generation/model/python/nr_cell_du.py @@ -28,6 +28,9 @@ from network_generation.model.python.geo_location import ( ) from network_generation.model.python.o_ran_node import IORanNode, ORanNode from network_generation.model.python.point import Point +from network_generation.model.python.o_ran_termination_point import ( + ORanTerminationPoint, +) # Define the "INrCellDu" interface @@ -55,8 +58,6 @@ default_value: INrCellDu = cast( # Define an abstract O-RAN Node class class NrCellDu(ORanNode): - _interfaces = ["cell"] - def __init__( self, data: dict[str, Any] = cast(dict[str, Any], default_value), @@ -64,6 +65,7 @@ class NrCellDu(ORanNode): ) -> None: cell_data: INrCellDu = self._to_cell_data(data) super().__init__(cast(dict[str, Any], cell_data), **kwargs) + self.type = "o-ran-sc-network:cell" self._cell_angle: int = int(str(cell_data["cellAngle"])) self._cell_scale_factor: int = int( str(cell_data["cellScaleFactorForHandoverArea"]) @@ -224,6 +226,15 @@ class NrCellDu(ORanNode): def get_geojson_href(self) -> str: return f"https://{self.network.host}/area/o-ran-network.geo.json/features?properties.name={self.name}" + def to_termination_point(self) -> ORanTerminationPoint: + tp = ORanTerminationPoint({ + "name": self.name, + "type": self.type, + "operationalState": self.operationalState, + "network": self.network + }) + return tp + def to_geojson_feature(self) -> list[dict[str, Any]]: cell_polygon: list[GeoLocation] = self.get_cell_polygons() coordinates: list = [] @@ -237,7 +248,7 @@ class NrCellDu(ORanNode): "type": "PropertiesOdu", "name": self.name, "uuid": self.id, - "function": "o-ran-sc-network:cell", + "function": self.type, "newRadioCellGlobalIdentity": { "publicLandMobileNetworkIdentifier": "123-45", "newRadioCellIdentity": "0x0FFFFFFFFF", diff --git a/code/network-generator/network_generation/model/python/o_ran_ru.py b/code/network-generator/network_generation/model/python/o_ran_ru.py index 944b574..a49aecf 100644 --- a/code/network-generator/network_generation/model/python/o_ran_ru.py +++ b/code/network-generator/network_generation/model/python/o_ran_ru.py @@ -46,7 +46,7 @@ default_value: IORanRu = cast( # Define an abstract O-RAN Node class class ORanRu(ORanNode): - _interfaces = ["ofhc", "ofhu", "ofhs", "ofhm", "cell"] + _interfaces = ["ofhc", "ofhu", "ofhs", "ofhm"] def __init__( self, @@ -104,7 +104,7 @@ class ORanRu(ORanNode): for index in range(self._cell_count): s: str = "00" + str(index) name: str = "-".join( - [self.name.replace("RU", "NRCellDu"), s[len(s) - 2: len(s)]] + [self.name, s[len(s) - 2: len(s)], "CELL"] ) azimuth: int = index * cell_angle + self._ru_azimuth result.append( @@ -135,17 +135,24 @@ class ORanRu(ORanNode): return self._oRanDu def to_topology_nodes(self) -> list[dict[str, Any]]: + # cells should be interpreted as termination points + for cell in self.cells: + cell_tp = cell.to_termination_point() + self.termination_points().append(cell_tp) result: list[dict[str, Any]] = super().to_topology_nodes() result.extend(self.oRanDu.to_topology_nodes()) return result + def to_tmf686_vertex(self) -> list[dict[str, Any]]: + result: list[dict[str, Any]] = super().to_tmf686_vertex() + result.extend(self.oRanDu.to_tmf686_vertex()) + return result + def to_topology_links(self) -> list[dict[str, Any]]: result: list[dict[str, Any]] = super().to_topology_links() result.extend(self.oRanDu.to_topology_links()) for interface in ["phy", "ofhm", "ofhc", "ofhu", "ofhs"]: - link_id: str = "".join( - [interface, ":", self.name, "<->", self.oRanDu.name] - ) + link_id: str = "".join([interface, ":", self.name, "<->", self.oRanDu.name]) source_tp: str = "-".join([self.name, interface.upper()]) dest_tp: str = "-".join([self.oRanDu.name, interface.upper()]) result.append( @@ -191,8 +198,7 @@ class ORanRu(ORanNode): """ Helper method to extend results with references from cells. - :param super_method: The superclass method to call for the initial - result. + :param super_method: The superclass method to call for the initial result. :param cell_method_name: The method name to call on each cell. :return: A list of dictionaries with the combined results. """ @@ -312,6 +318,50 @@ class ORanRu(ORanNode): ) return result + def toKml(self) -> ET.Element: + o_ran_ru: ET.Element = ET.Element("Folder") + open: ET.Element = ET.SubElement(o_ran_ru, "open") + open.text = "1" + name: ET.Element = ET.SubElement(o_ran_ru, "name") + name.text = self.name + for cell in self.cells: + o_ran_ru.append(cell.toKml()) + return o_ran_ru + + def toSvg(self) -> ET.Element: + return ET.Element("to-be-implemented") + + def to_directory(self, parent_dir: str) -> None: + self.oRanDu.to_directory(parent_dir) + parent_path = os.path.join(parent_dir, self.type) + path = os.path.join(parent_path, self.name) + if not os.path.exists(parent_path): + os.makedirs(parent_path, exist_ok=True) + if not os.path.exists(path): + os.mkdir(path) + + def _extend_with_cell_references( + self: Any, super_method: Any, cell_method_name: str + ) -> list[dict[str, Any]]: + """ + Helper method to extend results with references from cells. + + :param super_method: The superclass method to call for the initial result. + :param cell_method_name: The method name to call on each cell. + :return: A list of dictionaries with the combined results. + """ + result = super_method() + result.extend(getattr(self.oRanDu, cell_method_name)()) + for cell in self.cells: + result.extend(self.flatten_list(getattr(cell, cell_method_name)())) + return result + + def to_geojson_feature(self) -> list[dict[str, Any]]: + return self._extend_with_cell_references( + super().to_geojson_feature, + "to_geojson_feature", + ) + def to_tmf633_service_candidate_references(self) -> list[dict[str, Any]]: return self._extend_with_cell_references( super().to_tmf633_service_candidate_references, -- 2.16.6