From ba3fa2dccfb8582b539119332e71933aa71b7f4f Mon Sep 17 00:00:00 2001 From: Martin Skorupski Date: Fri, 22 Aug 2025 13:12:22 +0200 Subject: [PATCH] Network generation support of 1.n O-DU:O-RU IssueID: OAM-485 Change-Id: Ib7ed8b1dc65d45e0e4241a357d5f4f9812033426 Signed-off-by: Martin Skorupski --- ...igB00011.json => config.indigo.operator-a.json} | 30 ++++++-- ...fig00111.json => config.indigo.operator-b.json} | 34 ++++++--- ...igC00011.json => config.indigo.operator-c.json} | 28 ++++++-- .../configurations/config00211.json | 80 ---------------------- .../configurations/config11111.json | 80 ---------------------- .../configurations/configA00011.json | 80 ---------------------- .../configurations/configA00111.json | 80 ---------------------- .../configurations/configB00111.json | 80 ---------------------- .../configurations/configC00111.json | 80 ---------------------- .../model/jsonSchema/configuration.schema.json | 10 ++- .../model/python/o_ran_network.py | 24 ++++++- .../network_generation/model/python/o_ran_node.py | 55 +++++++++------ .../network_generation/model/python/o_ran_ru.py | 23 ++++++- .../network_generation/model/python/tower.py | 1 + .../network_generation/view/network_viewer.py | 69 +++++++++++++++++++ 15 files changed, 223 insertions(+), 531 deletions(-) rename code/network-generator/configurations/{configB00011.json => config.indigo.operator-a.json} (76%) rename code/network-generator/configurations/{config00111.json => config.indigo.operator-b.json} (73%) rename code/network-generator/configurations/{configC00011.json => config.indigo.operator-c.json} (77%) delete mode 100644 code/network-generator/configurations/config00211.json delete mode 100644 code/network-generator/configurations/config11111.json delete mode 100644 code/network-generator/configurations/configA00011.json delete mode 100644 code/network-generator/configurations/configA00111.json delete mode 100644 code/network-generator/configurations/configB00111.json delete mode 100644 code/network-generator/configurations/configC00111.json diff --git a/code/network-generator/configurations/configB00011.json b/code/network-generator/configurations/config.indigo.operator-a.json similarity index 76% rename from code/network-generator/configurations/configB00011.json rename to code/network-generator/configurations/config.indigo.operator-a.json index 1257e75..3f8f062 100644 --- a/code/network-generator/configurations/configB00011.json +++ b/code/network-generator/configurations/config.indigo.operator-a.json @@ -1,13 +1,13 @@ { "network": { - "name": "O-RAN-Network-Operator-B-small", + "name": "Operator-A", "host": "to-be-replaced-by-host-fqdn", - "version":"v0.0.2", + "version": "v0.0.2", "operationalState": "enabled", "center": { "latitude": 40.535, - "longitude": -74.457, - "aboveMeanSeaLevel": 52 + "longitude": -74.451464, + "aboveMeanSeaLevel": 50 }, "disabledResourcesProfile": { "o-ran-sc-network:smo": 0, @@ -29,7 +29,7 @@ "oCloudResourcePoolCount": 1 }, "nearRtRic": { - "oRanCuSpiralRadius":0 + "oRanCuSpiralRadius": 0 }, "oRanCu": { "oRanDuSpiralRadius": 0 @@ -46,7 +46,7 @@ "sectorCount": 1, "cellAngle": 120, "cellScaleFactorForHandoverArea": 20, - "maxReach": 400 + "maxReach": 2000 } } }, @@ -66,12 +66,28 @@ }, "kml": { "enabled": true, - "compressed": true + "compressed": false + }, + "tmf632": { + "enabled": true, + "compressed": false + }, + "tmf633": { + "enabled": true, + "compressed": false + }, + "tmf634": { + "enabled": true, + "compressed": false }, "rfc7946": { "enabled": true, "compressed": false }, + "tmf686": { + "enabled": true, + "compressed": false + }, "teiv": { "enabled": false, "compressed": false diff --git a/code/network-generator/configurations/config00111.json b/code/network-generator/configurations/config.indigo.operator-b.json similarity index 73% rename from code/network-generator/configurations/config00111.json rename to code/network-generator/configurations/config.indigo.operator-b.json index f511b54..5cb09c3 100644 --- a/code/network-generator/configurations/config00111.json +++ b/code/network-generator/configurations/config.indigo.operator-b.json @@ -1,13 +1,13 @@ { "network": { - "name": "O-RAN-Network-Operator-B", + "name": "Operator-B", "host": "to-be-replaced-by-host-fqdn", - "version":"v0.0.2", + "version": "v0.0.2", "operationalState": "enabled", "center": { - "latitude": 40.535, - "longitude": -74.457, - "aboveMeanSeaLevel": 52 + "latitude": 40.537810, + "longitude": -74.449362, + "aboveMeanSeaLevel": 50 }, "disabledResourcesProfile": { "o-ran-sc-network:smo": 0, @@ -29,10 +29,10 @@ "oCloudResourcePoolCount": 1 }, "nearRtRic": { - "oRanCuSpiralRadius":0 + "oRanCuSpiralRadius": 0 }, "oRanCu": { - "oRanDuSpiralRadius": 1 + "oRanDuSpiralRadius": 0 }, "oRanDu": { "fronthaulGatewayCount": 0, @@ -46,7 +46,7 @@ "sectorCount": 1, "cellAngle": 120, "cellScaleFactorForHandoverArea": 20, - "maxReach": 400 + "maxReach": 2000 } } }, @@ -66,12 +66,28 @@ }, "kml": { "enabled": true, - "compressed": true + "compressed": false + }, + "tmf632": { + "enabled": true, + "compressed": false + }, + "tmf633": { + "enabled": true, + "compressed": false + }, + "tmf634": { + "enabled": true, + "compressed": false }, "rfc7946": { "enabled": true, "compressed": false }, + "tmf686": { + "enabled": true, + "compressed": false + }, "teiv": { "enabled": false, "compressed": false diff --git a/code/network-generator/configurations/configC00011.json b/code/network-generator/configurations/config.indigo.operator-c.json similarity index 77% rename from code/network-generator/configurations/configC00011.json rename to code/network-generator/configurations/config.indigo.operator-c.json index d65bb41..95572d5 100644 --- a/code/network-generator/configurations/configC00011.json +++ b/code/network-generator/configurations/config.indigo.operator-c.json @@ -1,13 +1,13 @@ { "network": { - "name": "O-RAN-Network-Operator-C-small", + "name": "Operator-C", "host": "to-be-replaced-by-host-fqdn", - "version":"v0.0.2", + "version": "v0.0.2", "operationalState": "enabled", "center": { "latitude": 40.535, "longitude": -74.447, - "aboveMeanSeaLevel": 53 + "aboveMeanSeaLevel": 50 }, "disabledResourcesProfile": { "o-ran-sc-network:smo": 0, @@ -16,7 +16,7 @@ "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-common-identity-refs:o-ru-function": 25, "o-ran-sc-network:cell": 0 }, "pattern": { @@ -46,7 +46,7 @@ "sectorCount": 1, "cellAngle": 120, "cellScaleFactorForHandoverArea": 20, - "maxReach": 400 + "maxReach": 2400 } } }, @@ -66,12 +66,28 @@ }, "kml": { "enabled": true, - "compressed": true + "compressed": false + }, + "tmf632": { + "enabled": true, + "compressed": false + }, + "tmf633": { + "enabled": true, + "compressed": false + }, + "tmf634": { + "enabled": true, + "compressed": false }, "rfc7946": { "enabled": true, "compressed": false }, + "tmf686": { + "enabled": true, + "compressed": false + }, "teiv": { "enabled": false, "compressed": false diff --git a/code/network-generator/configurations/config00211.json b/code/network-generator/configurations/config00211.json deleted file mode 100644 index 40ce835..0000000 --- a/code/network-generator/configurations/config00211.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "network": { - "name": "O-RAN-Network-Operator-A", - "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": 2 - }, - "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/config11111.json b/code/network-generator/configurations/config11111.json deleted file mode 100644 index ab163fe..0000000 --- a/code/network-generator/configurations/config11111.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "network": { - "name": "O-RAN-Network-Operator-C", - "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": 1, - "oCloudResourcePoolCount": 1 - }, - "nearRtRic": { - "oRanCuSpiralRadius":1 - }, - "oRanCu": { - "oRanDuSpiralRadius": 1 - }, - "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/configA00011.json b/code/network-generator/configurations/configA00011.json deleted file mode 100644 index e7a7743..0000000 --- a/code/network-generator/configurations/configA00011.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "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/configA00111.json b/code/network-generator/configurations/configA00111.json deleted file mode 100644 index 9c3a752..0000000 --- a/code/network-generator/configurations/configA00111.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "network": { - "name": "O-RAN-Network-Operator-A", - "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": 1 - }, - "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/configB00111.json b/code/network-generator/configurations/configB00111.json deleted file mode 100644 index f511b54..0000000 --- a/code/network-generator/configurations/configB00111.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "network": { - "name": "O-RAN-Network-Operator-B", - "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": 1 - }, - "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/configC00111.json b/code/network-generator/configurations/configC00111.json deleted file mode 100644 index d2fb25a..0000000 --- a/code/network-generator/configurations/configC00111.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "network": { - "name": "O-RAN-Network-Operator-C", - "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": 1 - }, - "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/jsonSchema/configuration.schema.json b/code/network-generator/network_generation/model/jsonSchema/configuration.schema.json index fdbfd44..c5de429 100644 --- a/code/network-generator/network_generation/model/jsonSchema/configuration.schema.json +++ b/code/network-generator/network_generation/model/jsonSchema/configuration.schema.json @@ -223,6 +223,7 @@ "type": "integer", "enum": [ 1, + 3, 7, 19, 37 @@ -283,8 +284,8 @@ "maxReach": { "description": "The maximal distance a UE can reach the cell in meters. The value also defines the radius around a tower where UEs may select a NRCellDU of an oRanRu mounted at this tower.", "type": "integer", - "minimum": 15, - "maximum": 600 + "minimum": 300, + "maximum": 6000 } } } @@ -413,7 +414,10 @@ "OperationalState": { "type": "string", "description": "Enumerated operational states aligned with ITU-T usage (e.g., M.3100 and M.3160). References: ITU-T M.3100: 'Generic Managed Entities', ITU-T M.3160: 'Generic Management Information Model'", - "enum": ["disabled", "enabled"] + "enum": [ + "disabled", + "enabled" + ] } } } \ No newline at end of file diff --git a/code/network-generator/network_generation/model/python/o_ran_network.py b/code/network-generator/network_generation/model/python/o_ran_network.py index af67aef..a7b817e 100644 --- a/code/network-generator/network_generation/model/python/o_ran_network.py +++ b/code/network-generator/network_generation/model/python/o_ran_network.py @@ -27,7 +27,7 @@ from network_generation.model.python.geo_location import ( GeoLocation, IGeoLocation, ) -from network_generation.model.python.hexagon import Layout +from network_generation.model.python.hexagon import Hex, Layout from network_generation.model.python.o_ran_object import ( IORanObject, ORanObject, @@ -80,6 +80,7 @@ class ORanNetwork(ORanObject): f'network and IoT services for the year ' f'{self.__current_time.strftime("%Y")}.' ) + self.type = "o-ran-sc-network:network" self._center: IGeoLocation = cast(IGeoLocation, configuration["center"]) # Calculate layout size using the configuration values. nr_cell_du = configuration["pattern"]["nrCellDu"] @@ -103,6 +104,7 @@ class ORanNetwork(ORanObject): self._o_ran_smo = ORanSmo({ "name": "O-RAN-SMO", "geoLocation": self.center, + "position": Hex(0, 0, 0), "layout": layout, "parent": self, "network": self, @@ -200,6 +202,14 @@ class ORanNetwork(ORanObject): return False # UUID not found + def __remove_duplicates(self, data, identifier_name = 'id'): + seen = {} + for item in data: + uuid = item.get(identifier_name) + if uuid not in seen: + seen[uuid] = item + return list(seen.values()) + def to_topology(self) -> Dict[str, Any]: """ Generate and return the network topology as a dictionary. @@ -207,6 +217,7 @@ class ORanNetwork(ORanObject): profile = self.configuration.get("disabledResourcesProfile", {}) resource_types = profile.keys() nodes: List[Dict[str, Any]] = self._o_ran_smo.to_topology_nodes() + nodes = self.__remove_duplicates(nodes, "o-ran-sc-network:uuid") # Initialize identifier lists for each resource type. identifier_lists: Dict[str, List[str]] = {identifier: [] for identifier in resource_types} @@ -244,6 +255,7 @@ class ORanNetwork(ORanObject): ) links: List[Dict[str, Any]] = self._o_ran_smo.to_topology_links() + links = self.__remove_duplicates(links, "link-id") return { "ietf-network:networks": { "network": [ @@ -311,7 +323,15 @@ class ORanNetwork(ORanObject): def to_geojson(self) -> dict[str, Any]: features: list[dict[str, Any]] = self._o_ran_smo.to_geojson_feature() - # links: list[dict[str, Any]] = self._o_ran_smo.to_topology_links() + # remove duplicates + seen = {} + for feature in features: + uuid = feature.get('properties').get('node-uuid') + if uuid is None: + uuid = feature.get('properties').get('uuid') + if uuid not in seen: + seen[uuid] = feature + features = list(seen.values()) return { "type": "FeatureCollection", "features": features, diff --git a/code/network-generator/network_generation/model/python/o_ran_node.py b/code/network-generator/network_generation/model/python/o_ran_node.py index d114e0c..0b544aa 100644 --- a/code/network-generator/network_generation/model/python/o_ran_node.py +++ b/code/network-generator/network_generation/model/python/o_ran_node.py @@ -299,29 +299,42 @@ class ORanNode(ORanObject): str("%.6f" % float(point_gl.aboveMeanSeaLevel)), ]) - # link to parent - if (getattr(self.parent, 'geo_location', None) is not None): - line: ET.Element = ET.SubElement(multi_geometry, "LineString") - extrude: ET.Element = ET.SubElement(line, "extrude") - extrude.text = "1" - tessellate: ET.Element = ET.SubElement(line, "tessellate") - tessellate.text = "1" - coordinates: ET.Element = ET.SubElement(line, "coordinates") - + # link to parent only if geo_location is not None + if getattr(self.parent, 'geo_location', None) is not None: my_gl = self.geo_location parent_gl = self.parent.geo_location - coordinates.text = " ".join([ - ",".join([ - str("%.6f" % float(my_gl.longitude)), - str("%.6f" % float(my_gl.latitude)), - str("%.6f" % float(my_gl.aboveMeanSeaLevel)), - ]), - ",".join([ - str("%.6f" % float(parent_gl.longitude)), - str("%.6f" % float(parent_gl.latitude)), - str("%.6f" % float(parent_gl.aboveMeanSeaLevel)), - ]), - ]) + + # Define a small tolerance for floating point comparison + epsilon = 1e-6 + + # Check if coordinates are effectively equal + coords_are_equal = ( + abs(float(my_gl.longitude) - float(parent_gl.longitude)) < epsilon and + abs(float(my_gl.latitude) - float(parent_gl.latitude)) < epsilon and + abs(float(my_gl.aboveMeanSeaLevel) - float(parent_gl.aboveMeanSeaLevel)) < epsilon + ) + + # Only create the line if coordinates differ + if not coords_are_equal: + line: ET.Element = ET.SubElement(multi_geometry, "LineString") + extrude: ET.Element = ET.SubElement(line, "extrude") + extrude.text = "1" + tessellate: ET.Element = ET.SubElement(line, "tessellate") + tessellate.text = "1" + coordinates: ET.Element = ET.SubElement(line, "coordinates") + + coordinates.text = " ".join([ + ",".join([ + str("%.6f" % float(my_gl.longitude)), + str("%.6f" % float(my_gl.latitude)), + str("%.6f" % float(my_gl.aboveMeanSeaLevel)), + ]), + ",".join([ + str("%.6f" % float(parent_gl.longitude)), + str("%.6f" % float(parent_gl.latitude)), + str("%.6f" % float(parent_gl.aboveMeanSeaLevel)), + ]), + ]) return folder @abstractmethod 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 a49aecf..56f78eb 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 @@ -29,6 +29,7 @@ from network_generation.model.python.o_ran_node import IORanNode, ORanNode # Define the "IORanRu" interface class IORanRu(IORanNode): + index: int cellCount: int ruAngle: int ruAzimuth: int @@ -38,7 +39,7 @@ default_value: IORanRu = cast( IORanRu, { **ORanNode.default(), - **{"cellCount": 1, "ruAngle": 120, "ruAzimuth": 0}, + **{"cellCount": 1, "ruAngle": 120, "ruAzimuth": 0, "index": 0}, }, ) @@ -71,11 +72,27 @@ class ORanRu(ORanNode): if o_ran_ru_data and "ruAzimuth" in o_ran_ru_data else 0 ) + self._index: int = ( + int(str(o_ran_ru_data["index"])) + if o_ran_ru_data and "index" in o_ran_ru_data + else 0 + ) self._cells: list[NrCellDu] = self._create_cells() - name: str = self.name.replace("RU", "DU") + + oRuCountByDu: int = self.network.configuration['pattern']['oRanDu']['oRanRuCount'] + oDuName: str = "00" + if (self._index % oRuCountByDu == 0): + oDuIndex: int = self._index + oDuName: str = "00" + str(oDuIndex) + oDuName = oDuName[len(oDuName) - 2: len(oDuName)] + oDuName = self.parent.parent.name.replace("Cloud-", "") + "-" + oDuName + + # exception for a 1:1 relation between O-RU and O-DU + if (oRuCountByDu == 1): + oDuName = self.name.replace("RU", "DU") o_ran_du_data: dict[str, Any] = { - "name": name, + "name": oDuName, "geoLocation": self.parent.geo_location, "position": self.parent.position, "layout": self.layout, diff --git a/code/network-generator/network_generation/model/python/tower.py b/code/network-generator/network_generation/model/python/tower.py index 5be4820..61c3adf 100644 --- a/code/network-generator/network_generation/model/python/tower.py +++ b/code/network-generator/network_generation/model/python/tower.py @@ -101,6 +101,7 @@ class Tower(ORanNode): "cellCount": cell_count, "ruAngle": ru_angle, "ruAzimuth": ru_azimuth, + "index": index, } ) ) diff --git a/code/network-generator/network_generation/view/network_viewer.py b/code/network-generator/network_generation/view/network_viewer.py index a9f79a6..95d3993 100644 --- a/code/network-generator/network_generation/view/network_viewer.py +++ b/code/network-generator/network_generation/view/network_viewer.py @@ -19,6 +19,7 @@ Provides functions to convert the Network into different formats import gzip import json +import math import xml.etree.ElementTree as ET import zipfile from typing import Any, Callable @@ -85,6 +86,38 @@ class NetworkViewer: json.dump(content, jf, ensure_ascii=False, indent=2) print(f'File "{filename}" saved!') + def process_keys(self, input: dict[str, Any]): + for key, value in input.items() : + if key == "o-ran-sc-network:type": + if value in self.summary: + self.summary[value] = self.summary[value] + 1 + else: + self.summary[value] = 1 + + if (type(value) is list): + for index, item in enumerate(value): + if type(item) is dict: + self.process_keys(item) + else: + if type(value) is dict: + self.process_keys(value) + + + def __hexagon_area(self, side_length: float) -> float: + """ + Calculate the area of a regular hexagon given its side length. + + Formula: + A = (3 * sqrt(3) / 2) * a^2 + + Args: + a (float): side length of the hexagon + + Returns: + float: area of the hexagon + """ + return (3 * math.sqrt(3) / 2) * side_length * side_length + def rfc8345(self, filename: str, compressed: bool = True) -> None: """ Method saving the class content to a file in json format. @@ -96,6 +129,42 @@ class NetworkViewer: file_extension: str = "-operational.json" self.__save_on_disc(f"{filename}{file_extension}", compressed, output) + # print summary + self.summary = {} + self.process_keys(output) + cfg = self.__network.configuration + radius :int = ( + cfg.get("pattern", {}).get("nrCellDu", {}).get("maxReach", 0) + ) + angle :int = ( + cfg.get("pattern", {}).get("nrCellDu", {}).get("cellAngle", 360) + ) + + tower_area = self.__hexagon_area(radius/1000) + cell_area = tower_area * angle / 360 + network_area = tower_area * self.summary.get("o-ran-sc-network:tower", 0) + result = { + "count": self.summary, + "size": { + "max-distance-ue-tower": { + "unit": "m", + "value": radius + }, + "cell-area": { + "unit": "km²", + "value": round(cell_area, 3) + }, + "tower-area": { + "unit": "km²", + "value": round(tower_area, 3) + }, + "network-area": { + "unit": "km²", + "value": round(network_area, 3) } + } + } + print(json.dumps(result, sort_keys=False, indent=2).encode("utf-8").decode("unicode_escape")) + def teiv(self, filename: str, compressed: bool = True) -> None: """ Method for saving the class content as teiv cloud event -- 2.16.6