Network generation support of 1.n O-DU:O-RU 40/14840/1
authorMartin Skorupski <martin.skorupski@highstreet-technologies.com>
Fri, 22 Aug 2025 11:12:22 +0000 (13:12 +0200)
committerMartin Skorupski <martin.skorupski@highstreet-technologies.com>
Fri, 22 Aug 2025 11:13:55 +0000 (13:13 +0200)
IssueID: OAM-485
Change-Id: Ib7ed8b1dc65d45e0e4241a357d5f4f9812033426
Signed-off-by: Martin Skorupski <martin.skorupski@highstreet-technologies.com>
15 files changed:
code/network-generator/configurations/config.indigo.operator-a.json [moved from code/network-generator/configurations/configB00011.json with 76% similarity]
code/network-generator/configurations/config.indigo.operator-b.json [moved from code/network-generator/configurations/config00111.json with 73% similarity]
code/network-generator/configurations/config.indigo.operator-c.json [moved from code/network-generator/configurations/configC00011.json with 77% similarity]
code/network-generator/configurations/config00211.json [deleted file]
code/network-generator/configurations/config11111.json [deleted file]
code/network-generator/configurations/configA00011.json [deleted file]
code/network-generator/configurations/configA00111.json [deleted file]
code/network-generator/configurations/configB00111.json [deleted file]
code/network-generator/configurations/configC00111.json [deleted file]
code/network-generator/network_generation/model/jsonSchema/configuration.schema.json
code/network-generator/network_generation/model/python/o_ran_network.py
code/network-generator/network_generation/model/python/o_ran_node.py
code/network-generator/network_generation/model/python/o_ran_ru.py
code/network-generator/network_generation/model/python/tower.py
code/network-generator/network_generation/view/network_viewer.py

@@ -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
       }
     }
   },
     },
     "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
@@ -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,
         "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
       }
     }
   },
     },
     "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
@@ -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
       }
     }
   },
     },
     "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 (file)
index 40ce835..0000000
+++ /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 (file)
index ab163fe..0000000
+++ /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 (file)
index e7a7743..0000000
+++ /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 (file)
index 9c3a752..0000000
+++ /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 (file)
index f511b54..0000000
+++ /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 (file)
index d2fb25a..0000000
+++ /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
index fdbfd44..c5de429 100644 (file)
               "type": "integer",
               "enum": [
                 1,
+                3,
                 7,
                 19,
                 37
             "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
             }
           }
         }
     "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
index af67aef..a7b817e 100644 (file)
@@ -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,
index d114e0c..0b544aa 100644 (file)
@@ -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
index a49aecf..56f78eb 100644 (file)
@@ -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,
index 5be4820..61c3adf 100644 (file)
@@ -101,6 +101,7 @@ class Tower(ORanNode):
                         "cellCount": cell_count,
                         "ruAngle": ru_angle,
                         "ruAzimuth": ru_azimuth,
+                        "index": index,
                     }
                 )
             )
index a9f79a6..95d3993 100644 (file)
@@ -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