linting ... 42/12042/1
authorMartin Skorupski <martin.skorupski@highstreet-technologies.com>
Sun, 12 Nov 2023 17:11:41 +0000 (18:11 +0100)
committerMartin Skorupski <martin.skorupski@highstreet-technologies.com>
Sun, 12 Nov 2023 17:11:55 +0000 (18:11 +0100)
- lint py files

Issue-ID: OAM-388
Change-Id: I797cf34049e4765971ee67017ee06f78fedebddf
Signed-off-by: Martin Skorupski <martin.skorupski@highstreet-technologies.com>
24 files changed:
code/network-generator/network_generation/base.py
code/network-generator/network_generation/cli.py
code/network-generator/network_generation/model/python/countries.py
code/network-generator/network_generation/model/python/cube.py
code/network-generator/network_generation/model/python/geo_location.py
code/network-generator/network_generation/model/python/hexagon.py
code/network-generator/network_generation/model/python/nr_cell_du.py
code/network-generator/network_generation/model/python/o_ran_cloud_du.py
code/network-generator/network_generation/model/python/o_ran_cu.py
code/network-generator/network_generation/model/python/o_ran_du.py
code/network-generator/network_generation/model/python/o_ran_near_rt_ric.py
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_object.py
code/network-generator/network_generation/model/python/o_ran_ru.py
code/network-generator/network_generation/model/python/o_ran_smo.py
code/network-generator/network_generation/model/python/o_ran_spiral_radius_profile.py
code/network-generator/network_generation/model/python/o_ran_termination_point.py
code/network-generator/network_generation/model/python/point.py
code/network-generator/network_generation/model/python/top.py
code/network-generator/network_generation/model/python/tower.py
code/network-generator/network_generation/model/python/type_definitions.py
code/network-generator/network_generation/parameter_validator.py
code/network-generator/network_generation/view/network_viewer.py

index 7fe3964..bf1dab0 100644 (file)
@@ -35,7 +35,7 @@ class NetworkGenerator:
     __configuration: dict = {}
 
     # constructor
-    def __init__(self, configuration: dict):
+    def __init__(self, configuration: dict) -> None:
         self.__configuration = configuration
 
     # getters
index ada37e1..ad4188a 100644 (file)
 
 # inspired by https://github.com/rochacbruno/python-project-template
 
-"""CLI interface for network_generation project.
-
-Be creative! do whatever you want!
-
-- Install click or typer and create a CLI app
-- Use builtin argparse
-- Start a web application
-- Import things from your .base module
-"""
-
-
-"""
-Module as entry point to generate an ietf topology json
-"""
 import os
 import sys
 
 from network_generation.base import NetworkGenerator
+from network_generation.model.python.o_ran_network import ORanNetwork
 from network_generation.parameter_validator import ParameterValidator
 from network_generation.view.network_viewer import NetworkViewer
 
+"""
+CLI interface for network_generation project.
+Module as entry point to generate an ietf topology json
+"""
+
 
-def main():  # pragma: no cover
+def main() -> None:  # pragma: no cover
     """
     The main function executes on commands:
     `python -m network_generation`.
@@ -45,10 +37,12 @@ def main():  # pragma: no cover
     validator: ParameterValidator = ParameterValidator(sys.argv)
 
     if validator.is_valid():
-        configuration = validator.configuration()
-        generator = NetworkGenerator(configuration["network"])
-        network = generator.generate()
-        viewer = NetworkViewer(network)
+        configuration: dict = validator.configuration()
+        generator: NetworkGenerator = NetworkGenerator(
+            configuration["network"]
+        )
+        network: ORanNetwork = generator.generate()
+        viewer: NetworkViewer = NetworkViewer(network)
 
         output_folder: str = configuration["output-folder"]
         # If folder doesn't exist, then create it.
@@ -56,20 +50,21 @@ def main():  # pragma: no cover
             os.makedirs(output_folder)
 
         name: str = configuration["network"]["name"]
+        filename: str = ""
 
         # topology json
         if configuration["generation-tasks"]["topology"] is True:
-            filename: str = output_folder + "/" + name + "-operational.json"
+            filename = output_folder + "/" + name + "-operational.json"
             viewer.json().save(filename)
 
         # svg xml
         if configuration["generation-tasks"]["svg"] is True:
-            filename: str = output_folder + "/" + name + ".svg"
+            filename = output_folder + "/" + name + ".svg"
             viewer.svg(filename)
 
         # kml xml
         if configuration["generation-tasks"]["kml"] is True:
-            filename: str = output_folder + "/" + name + ".kml"
+            filename = output_folder + "/" + name + ".kml"
             viewer.kml(filename)
 
     else:
index 44e4eab..0f08909 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 A TypeDefinition as enum for countries
index 0d4e2a3..2c5ef06 100644 (file)
@@ -14,7 +14,7 @@
 #
 # inspired by http://www.redblobgames.com/grids/hexagons/
 
-#!/usr/bin/python
+# !/usr/bin/python
 from network_generation.model.python.hexagon import Hex
 
 
@@ -35,7 +35,8 @@ class Cube:
     def direction(direction: int) -> Hex:
         if direction < 0 or direction > 5:
             raise ValueError(
-                "Invalid direction. The direction value must be in the range of [0..5]."
+                "Invalid direction. The direction value must be"
+                + " in the range of [0..5]."
             )
         return Cube.direction_vectors()[direction]
 
@@ -55,7 +56,8 @@ class Cube:
     def ring(center: Hex, radius: int) -> list[Hex]:
         if not (radius > 0):
             raise ValueError(
-                "Invalid radius. The radius around the hex center must be greater than 0 rings."
+                "Invalid radius. The radius around the hex center must"
+                + " be greater than 0 rings."
             )
         results: list[Hex] = []
         hex: Hex = Cube.add(center, Cube.scale(Cube.direction(4), radius))
index 3b6793a..64449f8 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 A collection of TypeDefinitions for a geographical location
 """
 import math
+from typing import Any, TypedDict, cast
+
+from typing_extensions import Required
 
 from network_generation.model.python.point import Point
 
 
-class IGeoLocationData:
-    def __init__(
-        self, latitude: float, longitude: float, aboveMeanSeaLevel: float
-    ):
-        self.latitude = latitude
-        self.longitude = longitude
-        self.aboveMeanSeaLevel = aboveMeanSeaLevel
+class IGeoLocation(TypedDict):
+    latitude: Required[float]
+    longitude: Required[float]
+    aboveMeanSeaLevel: Required[float]
 
 
-class IGeoLocation:
-    def __init__(
-        self,
-        latitude: float = 0,
-        longitude: float = 0,
-        aboveMeanSeaLevel: float = 0,
-    ):
-        self.latitude = latitude
-        self.longitude = longitude
-        self.aboveMeanSeaLevel = aboveMeanSeaLevel
+default_value: IGeoLocation = {
+    "latitude": 0,
+    "longitude": 0,
+    "aboveMeanSeaLevel": 0,
+}
 
-    def __str__(self) -> str:
-        return f"lat : {self.latitude} : lon : {self.longitude} : amsl : {self.aboveMeanSeaLevel}"
 
+class GeoLocation:
+    @staticmethod
+    def default() -> dict[str, Any]:
+        return cast(dict[str, Any], default_value)
 
-class GeoLocation(IGeoLocation):
     _equatorialRadius = 6378137  # meters
     _polarRadius = 6356752  # meters
 
-    def __init__(self, geoLocation: IGeoLocationData = None):
-        super().__init__(
-            geoLocation["latitude"] if geoLocation else 0,
-            geoLocation["longitude"] if geoLocation else 0,
-            geoLocation["aboveMeanSeaLevel"] if geoLocation else 0,
-        )
+    def __init__(self, data: IGeoLocation = default_value) -> None:
+        self.latitude = data["latitude"]
+        self.longitude = data["longitude"]
+        self.aboveMeanSeaLevel = data["aboveMeanSeaLevel"]
 
     @property
     def equatorialRadius(self) -> int:
@@ -65,19 +59,40 @@ class GeoLocation(IGeoLocation):
     def polarRadius(self) -> int:
         return GeoLocation._polarRadius
 
-    def set_latitude(self, value: float):
+    @property
+    def latitude(self) -> float:
+        return self._latitude
+
+    @latitude.setter
+    def latitude(self, value: float) -> None:
         if not (-90 <= value <= 90):
+            msg: str = "Invalid latitude. Latitude must be between -90 and 90."
+            raise ValueError(msg)
+        self._latitude = value
+
+    @property
+    def longitude(self) -> float:
+        return self._longitude
+
+    @longitude.setter
+    def longitude(self, value: float) -> None:
+        if not (-180 <= value <= 180):
             raise ValueError(
-                "Invalid latitude. Latitude must be between -90 and 90."
+                "Invalid longitude. Longitude must be between -180 and 180."
             )
-        self.latitude = value
+        self._longitude = value
+
+    @property
+    def aboveMeanSeaLevel(self) -> float:
+        return self._aboveMeanSeaLevel
 
-    def set_longitude(self, value: float):
+    @aboveMeanSeaLevel.setter
+    def aboveMeanSeaLevel(self, value: float) -> None:
         if not (-180 <= value <= 180):
             raise ValueError(
                 "Invalid longitude. Longitude must be between -180 and 180."
             )
-        self.longitude = value
+        self._aboveMeanSeaLevel = value
 
     def json(self) -> dict[str, float]:
         return {
@@ -89,22 +104,21 @@ class GeoLocation(IGeoLocation):
     def __str__(self) -> str:
         return str(self.json())
 
-    def point_to_geo_location(self, point: Point):
+    def point_to_geo_location(self, point: Point) -> Any:
         """
-        A static function which converts a point in pixels into a geographical location
-        when the self is represented as Point(0,0)
+        A static function which converts a point in pixels into a geographical
+        location when the self is represented as Point(0,0)
         @param point : The point to be converted
         returns The converted GeoLocation object.
         """
-        equatorialRadius = 6378137  # meters
-        new_lat = self.latitude + (point.y / equatorialRadius) * (
+        new_lat = self.latitude + (point.y / self.equatorialRadius) * (
             180 / math.pi
         )
-        new_lon = self.longitude + (point.x / equatorialRadius) * (
+        new_lon = self.longitude + (point.x / self.equatorialRadius) * (
             180 / math.pi
         ) / math.cos(self.latitude * math.pi / 180)
 
-        geo_location: IGeoLocationData = {
+        geo_location: IGeoLocation = {
             "longitude": new_lon,
             "latitude": new_lat,
             "aboveMeanSeaLevel": self.aboveMeanSeaLevel,
index 5a81c23..555c601 100644 (file)
@@ -14,7 +14,7 @@
 #
 # inspired by http://www.redblobgames.com/grids/hexagons/
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 from __future__ import division, print_function
 
@@ -27,7 +27,7 @@ from network_generation.model.python.point import Point
 
 
 class Hex:
-    def __init__(self, q: int, r: int, s: int):
+    def __init__(self, q: float, r: float, s: float) -> None:
         if round(q + r + s) != 0:
             raise ValueError("The sum of q, r, and s must be 0.")
         self.q = q
@@ -50,11 +50,11 @@ def hex_scale(a: Hex, k: int) -> Hex:
     return Hex(a.q * k, a.r * k, a.s * k)
 
 
-def hex_rotate_left(a) -> Hex:
+def hex_rotate_left(a: Hex) -> Hex:
     return Hex(-a.s, -a.q, -a.r)
 
 
-def hex_rotate_right(a) -> Hex:
+def hex_rotate_right(a: Hex) -> Hex:
     return Hex(-a.r, -a.s, -a.q)
 
 
@@ -90,18 +90,18 @@ def hex_diagonal_neighbor(hex: Hex, direction: int) -> Hex:
     return hex_add(hex, hex_diagonals[direction])
 
 
-def hex_length(hex: Hex) -> int:
+def hex_length(hex: Hex) -> float:
     return (abs(hex.q) + abs(hex.r) + abs(hex.s)) // 2
 
 
-def hex_distance(a: Hex, b: Hex) -> int:
+def hex_distance(a: Hex, b: Hex) -> float:
     return hex_length(hex_subtract(a, b))
 
 
 def hex_round(hex: Hex) -> Hex:
-    qi = int(round(hex.q))
-    ri = int(round(hex.r))
-    si = int(round(hex.s))
+    qi = round(hex.q)
+    ri = round(hex.r)
+    si = round(hex.s)
     q_diff = abs(qi - hex.q)
     r_diff = abs(ri - hex.r)
     s_diff = abs(si - hex.s)
@@ -115,21 +115,21 @@ def hex_round(hex: Hex) -> Hex:
     return Hex(qi, ri, si)
 
 
-def hex_lerp(a: Hex, b: Hex, t: int) -> Hex:  # linearly interpolation
+def hex_lerp(a: Hex, b: Hex, t: float) -> Hex:  # linearly interpolation
     return Hex(
-        a.q * (1.0 - t) + b.q * t,
-        a.r * (1.0 - t) + b.r * t,
-        a.s * (1.0 - t) + b.s * t,
+        a.q * (1 - t) + b.q * t,
+        a.r * (1 - t) + b.r * t,
+        a.s * (1 - t) + b.s * t,
     )
 
 
-def hex_linedraw(a: Hex, b: Hex) -> list[hex]:
-    N = hex_distance(a, b)
-    a_nudge = Hex(a.q + 1e-06, a.r + 1e-06, a.s - 2e-06)
-    b_nudge = Hex(b.q + 1e-06, b.r + 1e-06, b.s - 2e-06)
-    results: list[hex] = []
-    step = 1.0 / max(N, 1)
-    for i in range(0, N + 1):
+def hex_linedraw(a: Hex, b: Hex) -> list[Hex]:
+    N: float = hex_distance(a, b)
+    a_nudge: Hex = Hex(a.q + 1e-06, a.r + 1e-06, a.s - 2e-06)
+    b_nudge: Hex = Hex(b.q + 1e-06, b.r + 1e-06, b.s - 2e-06)
+    results: list[Hex] = []
+    step: float = 1 / max(N, 1)
+    for i in range(0, int(N) + 1):
         results.append(hex_round(hex_lerp(a_nudge, b_nudge, step * i)))
     return results
 
@@ -140,33 +140,36 @@ EVEN: int = 1
 ODD: int = -1
 
 
-def qoffset_from_cube(offset: int, hex: Hex) -> OffsetCoord:
+def qoffset_from_cube(offset: float, hex: Hex) -> OffsetCoord:
     col = hex.q
-    row = hex.r + (hex.q + offset * (hex.q & 1)) // 2
+    row = hex.r + (hex.q + offset * (int(hex.q) & 1)) // 2
     if offset != EVEN and offset != ODD:
         raise ValueError("offset must be EVEN (+1) or ODD (-1)")
     return OffsetCoord(col, row)
 
 
-def qoffset_to_cube(offset: int, hex: Hex) -> Hex:
-    q = hex.col
-    r = hex.row - (hex.col + offset * (hex.col & 1)) // 2
+def qoffset_to_cube(offset: int, offsetCoord: OffsetCoord) -> Hex:
+    q = offsetCoord.col
+    r = (
+        offsetCoord.row
+        - (offsetCoord.col + offset * (offsetCoord.col & 1)) // 2
+    )
     s = -q - r
     if offset != EVEN and offset != ODD:
         raise ValueError("offset must be EVEN (+1) or ODD (-1)")
     return Hex(q, r, s)
 
 
-def roffset_from_cube(offset: int, hex: Hex) -> OffsetCoord:
-    col = hex.q + (hex.r + offset * (hex.r & 1)) // 2
+def roffset_from_cube(offset: float, hex: Hex) -> OffsetCoord:
+    col = hex.q + (hex.r + offset * (int(hex.r) & 1)) // 2
     row = hex.r
     if offset != EVEN and offset != ODD:
         raise ValueError("offset must be EVEN (+1) or ODD (-1)")
     return OffsetCoord(col, row)
 
 
-def roffset_to_cube(offset: int, hex: Hex) -> Hex:
-    q = hex.col - (hex.row + offset * (hex.row & 1)) // 2
+def roffset_to_cube(offset: float, hex: OffsetCoord) -> Hex:
+    q = hex.col - (hex.row + offset * (int(hex.row) & 1)) // 2
     r = hex.row
     s = -q - r
     if offset != EVEN and offset != ODD:
@@ -177,15 +180,15 @@ def roffset_to_cube(offset: int, hex: Hex) -> Hex:
 DoubledCoord = collections.namedtuple("DoubledCoord", ["col", "row"])
 
 
-def qdoubled_from_cube(hex: Hex):
+def qdoubled_from_cube(hex: Hex) -> DoubledCoord:
     col = hex.q
     row = 2 * hex.r + hex.q
     return DoubledCoord(col, row)
 
 
-def qdoubled_to_cube(hex: Hex) -> Hex:
-    q = hex.col
-    r = (hex.row - hex.col) // 2
+def qdoubled_to_cube(doubledCoord: DoubledCoord) -> Hex:
+    q = doubledCoord.col
+    r = (doubledCoord.row - doubledCoord.col) // 2
     s = -q - r
     return Hex(q, r, s)
 
@@ -196,9 +199,9 @@ def rdoubled_from_cube(hex: Hex) -> DoubledCoord:
     return DoubledCoord(col, row)
 
 
-def rdoubled_to_cube(hex: Hex):
-    q = (hex.col - hex.row) // 2
-    r = hex.row
+def rdoubled_to_cube(doubledCoord: DoubledCoord) -> Hex:
+    q = (doubledCoord.col - doubledCoord.row) // 2
+    r = doubledCoord.row
     s = -q - r
     return Hex(q, r, s)
 
@@ -279,284 +282,4 @@ def hex_to_geo_location(
     layout: Layout, hex: Hex, reference: GeoLocation
 ) -> GeoLocation:
     hexPoint: Point = hex_to_pixel(layout, hex)
-    return GeoLocation(reference).point_to_geo_location(hexPoint)
-
-
-# Tests
-
-
-def complain(name):
-    print("FAIL {0}".format(name))
-
-
-def equal_hex(name, a, b):
-    if not (a.q == b.q and a.s == b.s and a.r == b.r):
-        complain(name)
-
-
-def equal_offsetcoord(name, a, b):
-    if not (a.col == b.col and a.row == b.row):
-        complain(name)
-
-
-def equal_doubledcoord(name, a, b):
-    if not (a.col == b.col and a.row == b.row):
-        complain(name)
-
-
-def equal_int(name, a, b):
-    if not (a == b):
-        complain(name)
-
-
-def equal_hex_array(name, a, b):
-    equal_int(name, len(a), len(b))
-    for i in range(0, len(a)):
-        equal_hex(name, a[i], b[i])
-
-
-def test_hex_arithmetic():
-    equal_hex("hex_add", Hex(4, -10, 6), hex_add(Hex(1, -3, 2), Hex(3, -7, 4)))
-    equal_hex(
-        "hex_subtract",
-        Hex(-2, 4, -2),
-        hex_subtract(Hex(1, -3, 2), Hex(3, -7, 4)),
-    )
-
-
-def test_hex_direction():
-    equal_hex("hex_direction", Hex(0, -1, 1), hex_direction(2))
-
-
-def test_hex_neighbor():
-    equal_hex("hex_neighbor", Hex(1, -3, 2), hex_neighbor(Hex(1, -2, 1), 2))
-
-
-def test_hex_diagonal():
-    equal_hex(
-        "hex_diagonal", Hex(-1, -1, 2), hex_diagonal_neighbor(Hex(1, -2, 1), 3)
-    )
-
-
-def test_hex_distance():
-    equal_int("hex_distance", 7, hex_distance(Hex(3, -7, 4), Hex(0, 0, 0)))
-
-
-def test_hex_rotate_right():
-    equal_hex(
-        "hex_rotate_right", hex_rotate_right(Hex(1, -3, 2)), Hex(3, -2, -1)
-    )
-
-
-def test_hex_rotate_left():
-    equal_hex(
-        "hex_rotate_left", hex_rotate_left(Hex(1, -3, 2)), Hex(-2, -1, 3)
-    )
-
-
-def test_hex_round():
-    a = Hex(0.0, 0.0, 0.0)
-    b = Hex(1.0, -1.0, 0.0)
-    c = Hex(0.0, -1.0, 1.0)
-    equal_hex(
-        "hex_round 1",
-        Hex(5, -10, 5),
-        hex_round(hex_lerp(Hex(0.0, 0.0, 0.0), Hex(10.0, -20.0, 10.0), 0.5)),
-    )
-    equal_hex("hex_round 2", hex_round(a), hex_round(hex_lerp(a, b, 0.499)))
-    equal_hex("hex_round 3", hex_round(b), hex_round(hex_lerp(a, b, 0.501)))
-    equal_hex(
-        "hex_round 4",
-        hex_round(a),
-        hex_round(
-            Hex(
-                a.q * 0.4 + b.q * 0.3 + c.q * 0.3,
-                a.r * 0.4 + b.r * 0.3 + c.r * 0.3,
-                a.s * 0.4 + b.s * 0.3 + c.s * 0.3,
-            )
-        ),
-    )
-    equal_hex(
-        "hex_round 5",
-        hex_round(c),
-        hex_round(
-            Hex(
-                a.q * 0.3 + b.q * 0.3 + c.q * 0.4,
-                a.r * 0.3 + b.r * 0.3 + c.r * 0.4,
-                a.s * 0.3 + b.s * 0.3 + c.s * 0.4,
-            )
-        ),
-    )
-
-
-def test_hex_linedraw():
-    equal_hex_array(
-        "hex_linedraw",
-        [
-            Hex(0, 0, 0),
-            Hex(0, -1, 1),
-            Hex(0, -2, 2),
-            Hex(1, -3, 2),
-            Hex(1, -4, 3),
-            Hex(1, -5, 4),
-        ],
-        hex_linedraw(Hex(0, 0, 0), Hex(1, -5, 4)),
-    )
-
-
-def test_layout():
-    h = Hex(3, 4, -7)
-    flat = Layout(layout_flat, Point(10.0, 15.0), Point(35.0, 71.0))
-    equal_hex(
-        "layout", h, hex_round(pixel_to_hex(flat, hex_to_pixel(flat, h)))
-    )
-    pointy = Layout(layout_pointy, Point(10.0, 15.0), Point(35.0, 71.0))
-    equal_hex(
-        "layout", h, hex_round(pixel_to_hex(pointy, hex_to_pixel(pointy, h)))
-    )
-
-
-def test_offset_roundtrip():
-    a = Hex(3, 4, -7)
-    b = OffsetCoord(1, -3)
-    equal_hex(
-        "conversion_roundtrip even-q",
-        a,
-        qoffset_to_cube(EVEN, qoffset_from_cube(EVEN, a)),
-    )
-    equal_offsetcoord(
-        "conversion_roundtrip even-q",
-        b,
-        qoffset_from_cube(EVEN, qoffset_to_cube(EVEN, b)),
-    )
-    equal_hex(
-        "conversion_roundtrip odd-q",
-        a,
-        qoffset_to_cube(ODD, qoffset_from_cube(ODD, a)),
-    )
-    equal_offsetcoord(
-        "conversion_roundtrip odd-q",
-        b,
-        qoffset_from_cube(ODD, qoffset_to_cube(ODD, b)),
-    )
-    equal_hex(
-        "conversion_roundtrip even-r",
-        a,
-        roffset_to_cube(EVEN, roffset_from_cube(EVEN, a)),
-    )
-    equal_offsetcoord(
-        "conversion_roundtrip even-r",
-        b,
-        roffset_from_cube(EVEN, roffset_to_cube(EVEN, b)),
-    )
-    equal_hex(
-        "conversion_roundtrip odd-r",
-        a,
-        roffset_to_cube(ODD, roffset_from_cube(ODD, a)),
-    )
-    equal_offsetcoord(
-        "conversion_roundtrip odd-r",
-        b,
-        roffset_from_cube(ODD, roffset_to_cube(ODD, b)),
-    )
-
-
-def test_offset_from_cube():
-    equal_offsetcoord(
-        "offset_from_cube even-q",
-        OffsetCoord(1, 3),
-        qoffset_from_cube(EVEN, Hex(1, 2, -3)),
-    )
-    equal_offsetcoord(
-        "offset_from_cube odd-q",
-        OffsetCoord(1, 2),
-        qoffset_from_cube(ODD, Hex(1, 2, -3)),
-    )
-
-
-def test_offset_to_cube():
-    equal_hex(
-        "offset_to_cube even-",
-        Hex(1, 2, -3),
-        qoffset_to_cube(EVEN, OffsetCoord(1, 3)),
-    )
-    equal_hex(
-        "offset_to_cube odd-q",
-        Hex(1, 2, -3),
-        qoffset_to_cube(ODD, OffsetCoord(1, 2)),
-    )
-
-
-def test_doubled_roundtrip():
-    a = Hex(3, 4, -7)
-    b = DoubledCoord(1, -3)
-    equal_hex(
-        "conversion_roundtrip doubled-q",
-        a,
-        qdoubled_to_cube(qdoubled_from_cube(a)),
-    )
-    equal_doubledcoord(
-        "conversion_roundtrip doubled-q",
-        b,
-        qdoubled_from_cube(qdoubled_to_cube(b)),
-    )
-    equal_hex(
-        "conversion_roundtrip doubled-r",
-        a,
-        rdoubled_to_cube(rdoubled_from_cube(a)),
-    )
-    equal_doubledcoord(
-        "conversion_roundtrip doubled-r",
-        b,
-        rdoubled_from_cube(rdoubled_to_cube(b)),
-    )
-
-
-def test_doubled_from_cube():
-    equal_doubledcoord(
-        "doubled_from_cube doubled-q",
-        DoubledCoord(1, 5),
-        qdoubled_from_cube(Hex(1, 2, -3)),
-    )
-    equal_doubledcoord(
-        "doubled_from_cube doubled-r",
-        DoubledCoord(4, 2),
-        rdoubled_from_cube(Hex(1, 2, -3)),
-    )
-
-
-def test_doubled_to_cube():
-    equal_hex(
-        "doubled_to_cube doubled-q",
-        Hex(1, 2, -3),
-        qdoubled_to_cube(DoubledCoord(1, 5)),
-    )
-    equal_hex(
-        "doubled_to_cube doubled-r",
-        Hex(1, 2, -3),
-        rdoubled_to_cube(DoubledCoord(4, 2)),
-    )
-
-
-def test_all():
-    test_hex_arithmetic()
-    test_hex_direction()
-    test_hex_neighbor()
-    test_hex_diagonal()
-    test_hex_distance()
-    test_hex_rotate_right()
-    test_hex_rotate_left()
-    test_hex_round()
-    test_hex_linedraw()
-    test_layout()
-    test_offset_roundtrip()
-    test_offset_from_cube()
-    test_offset_to_cube()
-    test_doubled_roundtrip()
-    test_doubled_from_cube()
-    test_doubled_to_cube()
-    print("test finished")
-
-
-if __name__ == "__main__":
-    test_all()
+    return reference.point_to_geo_location(hexPoint)
index a0fb6e8..40fa66b 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 A Class representing a 3GPP new radio cell du (NrCellDu)
 """
 import xml.etree.ElementTree as ET
-from typing import overload
+from typing import Any, cast
 
 import network_generation.model.python.hexagon as Hexagon
-from network_generation.model.python.geo_location import GeoLocation
-from network_generation.model.python.o_ran_node import ORanNode
-from network_generation.model.python.o_ran_object import IORanObject
+from network_generation.model.python.geo_location import (
+    GeoLocation,
+    IGeoLocation,
+)
+from network_generation.model.python.o_ran_node import IORanNode, ORanNode
 from network_generation.model.python.o_ran_termination_point import (
     ORanTerminationPoint,
 )
@@ -31,42 +33,54 @@ from network_generation.model.python.point import Point
 
 
 # Define the "INrCellDu" interface
-class INrCellDu(IORanObject):
-    def __init__(self, cell_angel: int, azimuth: int, **kwargs):
-        super().__init__(**kwargs)
-        self._cell_angle = cell_angel
-        self._azimuth = azimuth
+class INrCellDu(IORanNode):
+    cell_angle: int
+    azimuth: int
+
+
+default_value: INrCellDu = cast(
+    INrCellDu,
+    {
+        **ORanNode.default(),
+        **{"cellAngle": 120, "azimuth": 120},
+    },
+)
 
 
 # Define an abstract O-RAN Node class
-class NrCellDu(ORanNode, INrCellDu):
-    def __init__(self, cell_data: INrCellDu = None, **kwargs):
-        super().__init__(cell_data, **kwargs)
-        self._cell_angle = (
-            cell_data["cellAngle"]
-            if cell_data and "cellAngle" in cell_data
-            else 120
-        )
-        self._azimuth = (
-            cell_data["azimuth"] if cell_data and "azimuth" in cell_data else 0
-        )
+class NrCellDu(ORanNode):
+    def __init__(
+        self,
+        data: dict[str, Any] = cast(dict[str, Any], default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        cell_data: INrCellDu = self._to_cell_data(data)
+        super().__init__(cast(dict[str, Any], cell_data), **kwargs)
+        self._cell_angle: int = int(str(cell_data["cellAngle"]))
+        self._azimuth: int = int(str(cell_data["azimuth"]))
+
+    def _to_cell_data(self, data: dict[str, Any]) -> INrCellDu:
+        result: INrCellDu = default_value
+        for key, key_type in INrCellDu.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
 
-    @property
     def termination_points(self) -> list[ORanTerminationPoint]:
-        result: list[ORanTerminationPoint] = super().termination_points
+        result: list[ORanTerminationPoint] = super().termination_points()
         result.append(
             ORanTerminationPoint({"id": self.name, "name": self.name})
         )
         return result
 
-    def to_topology_nodes(self) -> list[dict[str, dict]]:
+    def to_topology_nodes(self) -> list[dict[str, Any]]:
         # a cell is not a node it is a Termination Point
-        result: list[dict[str, dict]] = []  # super().to_topology_nodes()
+        result: list[dict[str, Any]] = []  # super().to_topology_nodes()
         return result
 
-    def to_topology_links(self) -> list[dict[str, dict]]:
+    def to_topology_links(self) -> list[dict[str, Any]]:
         # as a cell is not a node, it does not have links
-        result: list[dict[str, dict]] = []  # super().to_topology_links()
+        result: list[dict[str, Any]] = []  # super().to_topology_links()
         return result
 
     def toKml(self) -> ET.Element:
@@ -84,15 +98,16 @@ class NrCellDu(ORanNode, INrCellDu):
         points: list[Point] = Hexagon.polygon_corners(
             self.layout, self.position
         )
-        method = GeoLocation(
-            self.parent.parent.parent.parent.parent.parent.geoLocation
-        ).point_to_geo_location
+        method = (
+            self.parent.parent.parent.parent.parent.parent
+            .geo_location.point_to_geo_location
+        )
         geo_locations: list[GeoLocation] = list(map(method, points))
         text: list[str] = []
 
         index: int = 1 + int(self._azimuth / self._cell_angle)
-        network_center: GeoLocation = GeoLocation(
-            self.parent.parent.parent.parent.parent.parent.geoLocation
+        network_center: GeoLocation = (
+            self.parent.parent.parent.parent.parent.parent.geo_location
         )
 
         intersect1: Point = Point(
@@ -115,7 +130,8 @@ class NrCellDu(ORanNode, INrCellDu):
             network_center.point_to_geo_location(intersect2)
         )
 
-        tower: GeoLocation = GeoLocation(self.geoLocation)
+        tower: GeoLocation = GeoLocation(cast(IGeoLocation, self.geo_location))
+        # TODO: Why a cast is required
 
         cell_polygon: list[GeoLocation] = []
         cell_polygon.append(tower)
@@ -126,13 +142,17 @@ class NrCellDu(ORanNode, INrCellDu):
         # close polygon
         cell_polygon.append(tower)
 
-        for geo_location in cell_polygon:
-            text.append(
-                f"{'%.6f' % geo_location.longitude},{'%.6f' % geo_location.latitude},{'%.6f' % geo_location.aboveMeanSeaLevel}"
-            )
+        for gl in cell_polygon:
+            index += 1
+            strs: list[str] = [
+                str("%.6f" % float(gl.longitude)),
+                str("%.6f" % float(gl.latitude)),
+                str("%.6f" % float(gl.aboveMeanSeaLevel)),
+            ]
+            text.append(",".join(strs))
         coordinates.text = " ".join(text)
 
         return placemark
 
-    def toSvg(self) -> None:
-        return None
+    def toSvg(self) -> ET.Element:
+        return ET.Element("to-be-implemented")
index 0c5b782..b97f97e 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
-A Class representing an O-RAN O-Cloud resource pool for O-RAN distributed units (ORanDu)
+A Class representing an O-RAN O-Cloud resource pool for O-RAN distributed units
+(ORanDu).
 By default all O-RAN-DUs associated with the towers around  are deployed here.
-Maybe dedicated hardware is required to host O-DUs, but it is expected 
+Maybe dedicated hardware is required to host O-DUs, but it is expected
 that the O-Cloud mechanism and concepts can be applied here.
 """
 import xml.etree.ElementTree as ET
-from typing import overload
+from typing import Any, cast
 
 import network_generation.model.python.hexagon as Hexagon
 from network_generation.model.python.cube import Cube
+from network_generation.model.python.geo_location import GeoLocation
 from network_generation.model.python.hexagon import Hex
-from network_generation.model.python.o_ran_node import ORanNode
-from network_generation.model.python.o_ran_object import IORanObject
+from network_generation.model.python.o_ran_node import (
+    IORanNode,
+    ORanNode,
+    default_value,
+)
 from network_generation.model.python.o_ran_termination_point import (
     ORanTerminationPoint,
 )
 from network_generation.model.python.tower import Tower
 
-
 # Define the "IORanDu" interface
-class IORanCloudDu(IORanObject):
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
+IORanCloudDu = IORanNode
 
 
 # Implements a concrete O-RAN Node class
-class ORanCloudDu(ORanNode, IORanCloudDu):
-    def __init__(self, o_ran_du_data: IORanCloudDu = None, **kwargs):
-        super().__init__(o_ran_du_data, **kwargs)
+class ORanCloudDu(ORanNode):
+    def __init__(
+        self,
+        data: dict[str, Any] = cast(dict[str, Any], default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        o_ran_cloud_du_data: IORanCloudDu = self._to_o_ran_cloud_du_data(data)
+
+        super().__init__(cast(dict[str, Any], o_ran_cloud_du_data), **kwargs)
         self._towers: list[Tower] = self._calculate_towers()
 
+    def _to_o_ran_cloud_du_data(self, data: dict[str, Any]) -> IORanCloudDu:
+        result: IORanCloudDu = default_value
+        for key, key_type in IORanCloudDu.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
+
     def _calculate_towers(self) -> list[Tower]:
         hex_ring_radius: int = (
-            self.spiralRadiusProfile.oRanDuSpiralRadiusOfTowers
+            self.parent.parent.parent.parent
+            .spiral_radius_profile.oRanDuSpiralRadiusOfTowers
         )
         hex_list: list[Hex] = Cube.spiral(self.position, hex_ring_radius)
         result: list[Tower] = []
@@ -57,10 +73,12 @@ class ORanCloudDu(ORanNode, IORanCloudDu):
             name: str = "-".join(
                 [
                     self.name.replace("O-Cloud-DU", "Tower"),
-                    s[len(s) - 2 : len(s)],
+                    s[len(s) - 2: len(s)],
                 ]
             )
-            network_center: dict = self.parent.parent.parent.parent.center
+            network_center: GeoLocation = (
+                self.parent.parent.parent.parent.center
+            )
             newGeo = Hexagon.hex_to_geo_location(
                 self.layout, hex, network_center
             ).json()
@@ -71,7 +89,6 @@ class ORanCloudDu(ORanNode, IORanCloudDu):
                         "geoLocation": newGeo,
                         "position": hex,
                         "layout": self.layout,
-                        "spiralRadiusProfile": self.spiralRadiusProfile,
                         "parent": self,
                     }
                 )
@@ -82,9 +99,8 @@ class ORanCloudDu(ORanNode, IORanCloudDu):
     def towers(self) -> list[Tower]:
         return self._towers
 
-    @property
     def termination_points(self) -> list[ORanTerminationPoint]:
-        result: list[ORanTerminationPoint] = super().termination_points
+        result: list[ORanTerminationPoint] = super().termination_points()
         phy_tp: str = "-".join([self.name, "phy".upper()])
         result.append(ORanTerminationPoint({"id": phy_tp, "name": phy_tp}))
         for interface in ["o2"]:
@@ -96,14 +112,14 @@ class ORanCloudDu(ORanNode, IORanCloudDu):
             )
         return result
 
-    def to_topology_nodes(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_nodes()
+    def to_topology_nodes(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_nodes()
         for tower in self.towers:
             result.extend(tower.to_topology_nodes())
         return result
 
-    def to_topology_links(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_links()
+    def to_topology_links(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_links()
         for tower in self.towers:
             result.extend(tower.to_topology_links())
         return result
@@ -118,5 +134,5 @@ class ORanCloudDu(ORanNode, IORanCloudDu):
             o_ran_cloud_du.append(tower.toKml())
         return o_ran_cloud_du
 
-    def toSvg(self) -> None:
-        return None
+    def toSvg(self) -> ET.Element:
+        return ET.Element("to-be-implemented")
index d3034b4..be0006d 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 A Class representing an O-RAN centralized unit (ORanCu)
 and at the same time a location for an O-Cloud resource pool
 """
 import xml.etree.ElementTree as ET
-from typing import overload
+from typing import Any, cast
 
 import network_generation.model.python.hexagon as Hexagon
-from network_generation.model.python.cube import Cube
+from network_generation.model.python.geo_location import GeoLocation
 from network_generation.model.python.hexagon import Hex
 from network_generation.model.python.o_ran_cloud_du import ORanCloudDu
-from network_generation.model.python.o_ran_node import ORanNode
-from network_generation.model.python.o_ran_object import IORanObject
+from network_generation.model.python.o_ran_node import (
+    IORanNode,
+    ORanNode,
+    default_value,
+)
 from network_generation.model.python.o_ran_termination_point import (
     ORanTerminationPoint,
 )
 from network_generation.model.python.tower import Tower
 
-
 # Define the "IORanCu" interface
-class IORanCu(IORanObject):
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
+IORanCu = IORanNode
 
 
 # Define an abstract O-RAN Node class
-class ORanCu(ORanNode, IORanCu):
-    def __init__(self, o_ran_cu_data: IORanCu = None, **kwargs):
-        super().__init__(o_ran_cu_data, **kwargs)
-        self._o_ran_cloud_dus: list[ORanCu] = self._calculate_o_ran_dus()
+class ORanCu(ORanNode):
+    def __init__(
+        self,
+        data: dict[str, Any] = cast(dict[str, Any], default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        o_ran_cu_data: IORanCu = self._to_o_ran_cu_data(data)
+        super().__init__(cast(dict[str, Any], o_ran_cu_data), **kwargs)
+        self._o_ran_cloud_dus: list[ORanCloudDu] = self._calculate_o_ran_dus()
+
+    def _to_o_ran_cu_data(self, data: dict[str, Any]) -> IORanCu:
+        result: IORanCu = default_value
+        for key, key_type in IORanCu.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
 
     def _calculate_o_ran_dus(self) -> list[ORanCloudDu]:
         hex_ring_radius: int = (
-            self.spiralRadiusProfile.oRanCuSpiralRadiusOfODus
+            self.parent.parent.parent
+            .spiral_radius_profile.oRanCuSpiralRadiusOfODus
         )
-        hex_list: list[Hex] = self.spiralRadiusProfile.oRanDuSpiral(
+        hex_list: list[
+            Hex
+        ] = self.parent.parent.parent.spiral_radius_profile.oRanDuSpiral(
             self.position, hex_ring_radius
         )
         result: list[ORanCloudDu] = []
         for index, hex in enumerate(hex_list):
             s: str = "00" + str(index)
             name: str = "-".join(
-                [self.name.replace("CU", "O-Cloud-DU"), s[len(s) - 2 : len(s)]]
+                [self.name.replace("CU", "O-Cloud-DU"), s[len(s) - 2: len(s)]]
             )
-            network_center: dict = self.parent.parent.parent.center
+            network_center: GeoLocation = self.parent.parent.parent.center
             newGeo = Hexagon.hex_to_geo_location(
                 self.layout, hex, network_center
             ).json()
@@ -69,7 +84,6 @@ class ORanCu(ORanNode, IORanCu):
                         "geoLocation": newGeo,
                         "position": hex,
                         "layout": self.layout,
-                        "spiralRadiusProfile": self.spiralRadiusProfile,
                         "parent": self,
                     }
                 )
@@ -88,11 +102,10 @@ class ORanCu(ORanNode, IORanCu):
                 result.append(tower)
         return result
 
-    @property
     def termination_points(self) -> list[ORanTerminationPoint]:
-        result: list[ORanTerminationPoint] = super().termination_points
+        result: list[ORanTerminationPoint] = super().termination_points()
         phy_tp: str = "-".join([self.name, "phy".upper()])
-        result.append({"tp-id": phy_tp, "name": phy_tp})
+        result.append(ORanTerminationPoint({"tp-id": phy_tp, "name": phy_tp}))
         for interface in ["e2", "o1"]:
             id: str = "-".join([self.name, interface.upper()])
             result.append(
@@ -102,16 +115,16 @@ class ORanCu(ORanNode, IORanCu):
             )
         return result
 
-    def to_topology_nodes(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_nodes()
+    def to_topology_nodes(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_nodes()
         # for o_ran_du in self.o_ran_dus: # TODO
         #     result.extend(o_ran_du.to_topology_nodes())
         for o_ran_cloud_du in self.o_ran_cloud_dus:
             result.extend(o_ran_cloud_du.to_topology_nodes())
         return result
 
-    def to_topology_links(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_links()
+    def to_topology_links(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_links()
         # for o_ran_du in self.o_ran_dus:
         # result.extend(o_ran_du.to_topology_links())
         for o_ran_cloud_du in self.o_ran_cloud_dus:
@@ -128,5 +141,5 @@ class ORanCu(ORanNode, IORanCu):
             o_ran_cu.append(o_ran_cloud_du.toKml())
         return o_ran_cu
 
-    def toSvg(self) -> None:
-        return None
+    def toSvg(self) -> ET.Element:
+        return ET.Element("to-be-implemented")
index 6e5149b..0392d2c 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 A Class representing an O-RAN distributed unit (ORanDu)
 """
 import xml.etree.ElementTree as ET
-from typing import overload
+from typing import Any, cast
 
-from network_generation.model.python.o_ran_node import ORanNode
-from network_generation.model.python.o_ran_object import IORanObject
+from network_generation.model.python.o_ran_node import IORanNode, ORanNode
 from network_generation.model.python.o_ran_termination_point import (
     ORanTerminationPoint,
 )
 
 
 # Define the "IORanDu" interface
-class IORanDu(IORanObject):
-    def __init__(self, o_ran_ru_count: int, **kwargs):
-        super().__init__(**kwargs)
-        self._o_ran_ru_count = o_ran_ru_count
+class IORanDu(IORanNode):
+    o_ran_ru_count: int
+
+
+default_value: IORanDu = cast(
+    IORanDu,
+    {
+        **ORanNode.default(),
+        **{"oRanRuCount": 1},
+    },
+)
 
 
 # Define an abstract O-RAN Node class
-class ORanDu(ORanNode, IORanDu):
-    def __init__(self, o_ran_du_data: IORanDu = None, **kwargs):
-        super().__init__(o_ran_du_data, **kwargs)
+class ORanDu(ORanNode):
+    def __init__(
+        self,
+        data: dict[str, Any] = cast(dict[str, Any], default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        o_ran_du_data: IORanDu = self._to_o_ran_du_data(data)
+        super().__init__(cast(dict[str, Any], o_ran_du_data), **kwargs)
         self._o_ran_ru_count = (
             o_ran_du_data["oRanRuCount"]
             if o_ran_du_data and "oRanRuCount" in o_ran_du_data
             else 1
         )
 
-    @property
+    def _to_o_ran_du_data(self, data: dict[str, Any]) -> IORanDu:
+        result: IORanDu = default_value
+        for key, key_type in IORanDu.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
+
     def termination_points(self) -> list[ORanTerminationPoint]:
-        result: list[ORanTerminationPoint] = super().termination_points
+        result: list[ORanTerminationPoint] = super().termination_points()
         phy_tp: str = "-".join([self.name, "phy".upper()])
         result.append(ORanTerminationPoint({"id": phy_tp, "name": phy_tp}))
         for interface in ["e2", "o1", "ofhm", "ofhc", "ofhu", "ofhs"]:
@@ -58,12 +75,12 @@ class ORanDu(ORanNode, IORanDu):
             )
         return result
 
-    def to_topology_nodes(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_nodes()
+    def to_topology_nodes(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_nodes()
         return result
 
-    def to_topology_links(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_links()
+    def to_topology_links(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_links()
         for interface in ["e2", "o1"]:
             link_id: str = "".join(
                 [interface, ":", self.name, "<->", self.parent.name]
@@ -91,9 +108,9 @@ class ORanDu(ORanNode, IORanDu):
         open.text = "1"
         name: ET.Element = ET.SubElement(o_ran_du, "name")
         name.text = self.name
-        for tower in self.towers:
-            o_ran_du.append(tower.toKml())
+        for tower in self.towers:
+            o_ran_du.append(tower.toKml())
         return o_ran_du
 
-    def toSvg(self) -> None:
-        return None
+    def toSvg(self) -> ET.Element:
+        return ET.Element("to-be-implemented")
index 4e92a5b..5a44843 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
-A Class representing an O-RAN Near real-time intelligent controller (ORanNearRtRic)
+A Class representing an O-RAN Near real-time intelligent controller
+(ORanNearRtRic)
 """
 import xml.etree.ElementTree as ET
-from typing import overload
+from typing import Any, cast
 
 import network_generation.model.python.hexagon as Hexagon
+from network_generation.model.python.geo_location import GeoLocation
 from network_generation.model.python.hexagon import Hex
 from network_generation.model.python.o_ran_cu import ORanCu
-from network_generation.model.python.o_ran_node import ORanNode
-from network_generation.model.python.o_ran_object import IORanObject
+from network_generation.model.python.o_ran_node import (
+    IORanNode,
+    ORanNode,
+    default_value,
+)
 from network_generation.model.python.o_ran_termination_point import (
     ORanTerminationPoint,
 )
 from network_generation.model.python.tower import Tower
 
-
 # Define the "IORanNearRtRic" interface
-class IORanNearRtRic(IORanObject):
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
+IORanNearRtRic = IORanNode
 
 
 # Define an abstract O-RAN Node class
-class ORanNearRtRic(ORanNode, IORanNearRtRic):
+class ORanNearRtRic(ORanNode):
     def __init__(
-        self, o_ran_near_rt_ric_data: IORanNearRtRic = None, **kwargs
-    ):
-        super().__init__(o_ran_near_rt_ric_data, **kwargs)
+        self,
+        data: dict[str, Any] = cast(dict[str, Any], default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        o_ran_near_rt_ric_data: IORanNearRtRic = (
+            self._to_o_ran_near_rt_ric_data(data)
+        )
+        super().__init__(
+            cast(dict[str, Any], o_ran_near_rt_ric_data), **kwargs
+        )
         self._o_ran_cus: list[ORanCu] = self._calculate_o_ran_cus()
 
+    def _to_o_ran_near_rt_ric_data(
+        self, data: dict[str, Any]
+    ) -> IORanNearRtRic:
+        result: IORanNearRtRic = default_value
+        for key, key_type in IORanNearRtRic.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
+
     def _calculate_o_ran_cus(self) -> list[ORanCu]:
         hex_ring_radius: int = (
-            self.spiralRadiusProfile.oRanNearRtRicSpiralRadiusOfOCus
+            self.parent.parent
+            .spiral_radius_profile.oRanNearRtRicSpiralRadiusOfOCus
         )
-        hex_list: list[Hex] = self.spiralRadiusProfile.oRanCuSpiral(
+        hex_list: list[
+            Hex
+        ] = self.parent.parent.spiral_radius_profile.oRanCuSpiral(
             self.position, hex_ring_radius
         )
         result: list[ORanCu] = []
         for index, hex in enumerate(hex_list):
             s: str = "00" + str(index)
             name: str = "-".join(
-                [self.name.replace("NearRtRic", "CU"), s[len(s) - 2 : len(s)]]
+                [self.name.replace("NearRtRic", "CU"), s[len(s) - 2: len(s)]]
             )
-            network_center: dict = self.parent.parent.center
+            network_center: GeoLocation = self.parent.parent.center
             newGeo = Hexagon.hex_to_geo_location(
                 self.layout, hex, network_center
             ).json()
@@ -69,7 +90,6 @@ class ORanNearRtRic(ORanNode, IORanNearRtRic):
                         "geoLocation": newGeo,
                         "position": hex,
                         "layout": self.layout,
-                        "spiralRadiusProfile": self.spiralRadiusProfile,
                         "parent": self,
                     }
                 )
@@ -88,11 +108,10 @@ class ORanNearRtRic(ORanNode, IORanNearRtRic):
                 result.append(tower)
         return result
 
-    @property
     def termination_points(self) -> list[ORanTerminationPoint]:
-        result: list[ORanTerminationPoint] = super().termination_points
+        result: list[ORanTerminationPoint] = super().termination_points()
         phy_tp: str = "-".join([self.name, "phy".upper()])
-        result.append({"tp-id": phy_tp, "name": phy_tp})
+        result.append(ORanTerminationPoint({"tp-id": phy_tp, "name": phy_tp}))
         for interface in ["a1", "o1", "o2", "e2"]:
             id: str = "-".join([self.name, interface.upper()])
             result.append(
@@ -102,14 +121,14 @@ class ORanNearRtRic(ORanNode, IORanNearRtRic):
             )
         return result
 
-    def to_topology_nodes(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_nodes()
+    def to_topology_nodes(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_nodes()
         for o_ran_cu in self.o_ran_cus:
             result.extend(o_ran_cu.to_topology_nodes())
         return result
 
-    def to_topology_links(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_links()
+    def to_topology_links(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_links()
         for o_ran_cu in self.o_ran_cus:
             result.extend(o_ran_cu.to_topology_links())
         return result
@@ -124,5 +143,5 @@ class ORanNearRtRic(ORanNode, IORanNearRtRic):
             ric.append(o_ran_cu.toKml())
         return ric
 
-    def toSvg(self) -> None:
-        return None
+    def toSvg(self) -> ET.Element:
+        return ET.Element("to-be-implemented")
index bb39b61..3f816b3 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 """
 Module for a class representing a O-RAN Network
 """
 import xml.etree.ElementTree as ET
+from typing import Any, cast
 
 import network_generation.model.python.hexagon as Hexagon
+from network_generation.model.python.geo_location import (
+    GeoLocation,
+    IGeoLocation,
+)
 from network_generation.model.python.hexagon import Layout
 from network_generation.model.python.o_ran_object import (
     IORanObject,
@@ -30,25 +35,38 @@ from network_generation.model.python.o_ran_spiral_radius_profile import (
 )
 from network_generation.model.python.point import Point
 
+# Define the "IORanNetwork" interface
+IORanNetwork = IORanObject
+
 
 class ORanNetwork(ORanObject):
     """
     Class representing an O-RAN Network object.
     """
 
+    __my_default_value: IORanNetwork = cast(IORanNetwork, ORanObject.default())
+
     # constructor
     def __init__(
-        self, configuration: dict[str, dict], of: IORanObject = None, **kwargs
-    ):
-        super().__init__(of, **kwargs)
+        self,
+        configuration: dict[str, dict],
+        data: dict[str, Any] = cast(dict[str, Any], __my_default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        o_ran_network_data: IORanNetwork = self._to_o_ran_network_data(data)
+        super().__init__(cast(dict[str, Any], o_ran_network_data), **kwargs)
         self.__configuration = configuration
-        self.name = configuration["name"]
-        self.center = configuration["center"]
-        size = configuration["pattern"]["nr-cell-du"]["max-reach"]
+
+        self.name = str(configuration["name"])
+        self._center: IGeoLocation = cast(
+            IGeoLocation, configuration["center"]
+        )
+
+        size: int = int(configuration["pattern"]["nr-cell-du"]["max-reach"])
         layout = Layout(
             Hexagon.layout_flat, Point(size, size), Point(0, 0)
         )  # 1 pixel = 1 meter
-        spiral_radius_profile = SpiralRadiusProfile(
+        self._spiral_radius_profile: SpiralRadiusProfile = SpiralRadiusProfile(
             {
                 "oRanSmoSpiralRadiusOfNearRtRics": configuration["pattern"][
                     "smo"
@@ -69,22 +87,44 @@ class ORanNetwork(ORanObject):
                 "name": "O-RAN-SMO",
                 "geoLocation": self.center,
                 "layout": layout,
-                "spiralRadiusProfile": spiral_radius_profile,
                 "parent": self,
             }
         )
 
-    # getter
-    def configuration(self) -> dict[str, dict]:
+    def _to_o_ran_network_data(self, data: dict[str, Any]) -> IORanNetwork:
+        result: IORanNetwork = self.__my_default_value
+        for key, key_type in IORanNetwork.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
+
+    @property
+    def center(self) -> GeoLocation:
+        """
+        Getter for a json object representing the O-RAN Network.
+        :return O-RAN Network as json object.
+        """
+        return GeoLocation(self._center)
+
+    @property
+    def spiral_radius_profile(self) -> SpiralRadiusProfile:
+        """
+        Getter for a json object representing the SpiralRadiusProfile.
+        :return SpiralRadiusProfile.
+        """
+        return self._spiral_radius_profile
+
+    @property
+    def configuration(self) -> dict[str, Any]:
         """
         Getter for a json object representing the O-RAN Network.
         :return O-RAN Network as json object.
         """
         return self.__configuration
 
-    def to_topology(self) -> dict[str, dict]:
-        nodes: dict[str, dict] = self._o_ran_smo.to_topology_nodes()
-        links: dict[str, dict] = self._o_ran_smo.to_topology_links()
+    def to_topology(self) -> dict[str, Any]:
+        nodes: list[dict[str, Any]] = self._o_ran_smo.to_topology_nodes()
+        links: list[dict[str, Any]] = self._o_ran_smo.to_topology_links()
         return {
             "ietf-network:networks": {
                 "network": [
@@ -129,12 +169,15 @@ class ORanNetwork(ORanObject):
             xmlns="http://www.w3.org/2000/svg",
         )
         desc = ET.Element("desc")
-        # desc.text="\n context: " + str(self.id()) + "\n name: " + str(self.name())
+        desc.text = "\n context: " + self.id + "\n name: " + self.name
         root.append(desc)
 
         title = ET.Element("title")
-        title.text = self.configuration()["name"]
+        title.text = str(self.configuration["name"])
         root.append(title)
 
         # root.append(self.__context.svg(x, y))
         return root
+
+    def json(self) -> dict[str, Any]:
+        return super().json()
index 7e5ec63..e8471ab 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 An abstract Class for O-RAN Node
 """
-import json
 import xml.etree.ElementTree as ET
-from abc import abstractmethod, abstractproperty
-from typing import Any
+from abc import abstractmethod
+from typing import Any, cast
 
 import network_generation.model.python.hexagon as Hexagon
+from network_generation.model.python.countries import Country
 from network_generation.model.python.geo_location import GeoLocation
 from network_generation.model.python.hexagon import Hex, Layout
 from network_generation.model.python.o_ran_object import (
@@ -41,74 +41,98 @@ from network_generation.model.python.type_definitions import AddressType
 
 # Define the "IORanObject" interface
 class IORanNode(IORanObject):
-    def __init__(
-        self,
-        address: AddressType = None,
-        geoLocation: GeoLocation = None,
-        url: str = None,
-        position: Hex = None,
-        layout: Layout = None,
-        spiralRadiusProfile: SpiralRadiusProfile = None,
-        parent=None,
-        **kwargs
-    ):
-        super().__init__(**kwargs)
-        self.address = address
-        self.geoLocation = geoLocation
-        self.url = url
-        self.position = position
-        self.layout = layout
-        self.spiralRadiusProfile = (spiralRadiusProfile,)
-        self.parent = parent
+    address: AddressType
+    geoLocation: GeoLocation
+    url: str
+    position: Hex
+    layout: Layout
+    spiralRadiusProfile: SpiralRadiusProfile
+    parent: Any
+
+
+default_address: AddressType = {
+    "street": "highstreet",
+    "building": "none",
+    "city": "heaven",
+    "room": "frist",
+    "zip": "12345",
+    "state": "none",
+    "country": Country.Germany,
+}
+default_value: IORanNode = cast(
+    IORanNode,
+    {
+        **ORanObject.default(),
+        **{
+            "address": default_address,
+            "geoLocation": GeoLocation(),
+            "url": "non-url",
+            "position": Hex(0, 0, 0),
+            "layout": Layout(Hexagon.layout_flat, Point(1, 1), Point(0, 0)),
+            "spiralRadiusProfile": SpiralRadiusProfile(),
+            "parent": None,
+        },
+    },
+)
 
 
 # Define an abstract O-RAN Node class
-class ORanNode(ORanObject, IORanNode):
-    def __init__(self, of: IORanNode = None, **kwargs):
-        super().__init__(of, **kwargs)
-        self.address = of["address"] if of and "address" in of else None
-        self.geoLocation = (
-            of["geoLocation"] if of and "geoLocation" in of else GeoLocation()
-        )
-        self.url = of["url"] if of and "url" in of else self.id
-        self.position = (
-            of["position"] if of and "position" in of else Hex(0, 0, 0)
+class ORanNode(ORanObject):
+    @staticmethod
+    def default() -> dict[str, Any]:
+        return cast(dict[str, Any], default_value)
+
+    def __init__(
+        self,
+        data: dict[str, Any] = cast(dict[str, Any], default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        o_ran_node_data: IORanNode = self._to_o_ran_node_data(data)
+        super().__init__(cast(dict[str, Any], data), **kwargs)
+        self._address: AddressType = cast(
+            AddressType, o_ran_node_data["address"]
         )
-        self.layout = (
-            of["layout"]
-            if of and "layout" in of
-            else Layout(Hexagon.layout_flat, Point(1, 1), Point(0, 0))
+        self._geo_location: GeoLocation = cast(
+            GeoLocation, o_ran_node_data["geoLocation"]
         )
-        self.spiralRadiusProfile = (
-            of["spiralRadiusProfile"]
-            if of and "spiralRadiusProfile" in of
-            else SpiralRadiusProfile()
+        self._url: str = str(o_ran_node_data["url"])
+        self._position: Hex = cast(Hex, o_ran_node_data["position"])
+        self._layout: Layout = cast(Layout, o_ran_node_data["layout"])
+        self._spiral_radius_profile: SpiralRadiusProfile = cast(
+            SpiralRadiusProfile, o_ran_node_data["spiralRadiusProfile"]
         )
-        self.parent = of["parent"] if of and "parent" in of else None
+        self._parent: Any = o_ran_node_data["parent"]
         self._termination_points: list[ORanTerminationPoint] = []
 
+    def _to_o_ran_node_data(self, data: dict[str, Any]) -> IORanNode:
+        result: IORanNode = default_value
+        for key, key_type in IORanNode.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
+
     @property
-    def address(self) -> str:
+    def address(self) -> AddressType:
         return self._address
 
     @address.setter
-    def address(self, value: str):
+    def address(self, value: AddressType) -> None:
         self._address = value
 
     @property
-    def geoLocation(self) -> GeoLocation:
-        return self._geographicalLocation
+    def geo_location(self) -> GeoLocation:
+        return self._geo_location
 
-    @geoLocation.setter
-    def geoLocation(self, value: GeoLocation):
-        self._geographicalLocation = value
+    @geo_location.setter
+    def geo_location(self, value: GeoLocation) -> None:
+        self._geo_location = value
 
     @property
     def url(self) -> str:
         return self._url
 
     @url.setter
-    def url(self, value: str):
+    def url(self, value: str) -> None:
         self._url = value
 
     @property
@@ -116,7 +140,7 @@ class ORanNode(ORanObject, IORanNode):
         return self._position
 
     @position.setter
-    def position(self, value: Hex):
+    def position(self, value: Hex) -> None:
         self._position = value
 
     @property
@@ -124,16 +148,16 @@ class ORanNode(ORanObject, IORanNode):
         return self._layout
 
     @layout.setter
-    def layout(self, value: Layout):
+    def layout(self, value: Layout) -> None:
         self._layout = value
 
     @property
-    def spiralRadiusProfile(self) -> SpiralRadiusProfile:
-        return self._spiralRadiusProfile
+    def spiral_radius_profile(self) -> SpiralRadiusProfile:
+        return self._spiral_radius_profile
 
-    @spiralRadiusProfile.setter
-    def spiralRadiusProfile(self, value: SpiralRadiusProfile):
-        self._spiralRadiusProfile = value
+    @spiral_radius_profile.setter
+    def spiral_radius_profile(self, value: SpiralRadiusProfile) -> None:
+        self._spiral_radius_profile = value
 
     @property
     def parent(
@@ -142,34 +166,26 @@ class ORanNode(ORanObject, IORanNode):
         return self._parent
 
     @parent.setter
-    def parent(self, value: Any):
+    def parent(self, value: Any) -> None:
         self._parent = value
 
-    @abstractproperty
+    # @property
+    # @abstractmethod
     def termination_points(self) -> list[ORanTerminationPoint]:
         return self._termination_points
 
-    def json(self) -> dict[str, dict]:
-        result: dict = super().json()
-        result["address"] = self.address
-        result["geoLocation"] = self.geoLocation
-        result["url"] = self.url
-        result["layout"] = self.layout
-        result["spiralRadiusProfile"] = self.spiralRadiusProfile
-        result["parent"] = self.parent
-        return result
-
     @abstractmethod
-    def to_topology_nodes(self) -> list[dict[str, dict]]:
-        tps: list[dict[str, dict]] = []
-        for tp in self.termination_points:
+    def to_topology_nodes(self) -> list[dict[str, Any]]:
+        tps: list[dict[str, Any]] = []
+        for tp in self.termination_points():
             if (
                 str(type(tp))
-                == "<class 'model.python.o_ran_termination_point.ORanTerminationPoint'>"
+                == "<class 'model.python.o_ran_termination_point"
+                + ".ORanTerminationPoint'>"
             ):
                 tps.append(tp.to_topology())
 
-        result: list[dict[str, dict]] = []
+        result: list[dict[str, Any]] = []
         result.append(
             {
                 "node-id": self.name,
@@ -179,11 +195,11 @@ class ORanNode(ORanObject, IORanNode):
         return result
 
     @abstractmethod
-    def to_topology_links(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = []
+    def to_topology_links(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = []
         source_tp: str = "-".join([self.name, "phy".upper()])
         dest_tp: str = "-".join([self.parent.name, "phy".upper()])
-        if self.parent and not "Tower" in source_tp and not "Tower" in dest_tp:
+        if self.parent and "Tower" not in source_tp and "Tower" not in dest_tp:
             link_id: str = "".join(
                 ["phy", ":", self.name, "<->", self.parent.name]
             )
@@ -203,5 +219,5 @@ class ORanNode(ORanObject, IORanNode):
         pass
 
     @abstractmethod
-    def toSvg(self) -> ET.Element | None:
+    def toSvg(self) -> ET.Element:
         pass
index 3e367a6..ef74b30 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 An abstract Class for O-RAN Objects
 """
-from network_generation.model.python.top import ITop, Top
+from typing import Any, cast
 
+from network_generation.model.python.top import ITop, Top, default_value
 
 # Define the "IORanObject" interface
-class IORanObject(ITop):
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
+# class IORanObject(ITop):
+IORanObject = ITop
 
 
 # Define an abstract O-RAN Object class
-class ORanObject(Top, IORanObject):
-    def __init__(self, of: IORanObject = None, **kwargs):
-        super().__init__(of, **kwargs)
-
-    def json(self) -> dict[str, dict]:
-        result: dict[str, dict] = super().json()
-        return result
-
-    def __str__(self) -> str:
-        return str(self.json())
+class ORanObject(Top):
+    @staticmethod
+    def default() -> dict[str, Any]:
+        return cast(dict[str, Any], default_value)
+
+    def __init__(
+        self, data: dict[str, Any] = default(), **kwargs: dict[str, Any]
+    ) -> None:
+        super().__init__(data, **kwargs)
+
+    def json(self) -> dict[str, Any]:
+        return {
+            **super().json(),
+            "id": self.id,
+            "name": self.name,
+            "administrativeState": self.administrativeState,
+            "operationalState": self.operationalState,
+            "lifeCycleState": self.lifeCycleState,
+            "alarmState": self.alarmState,
+            "usageState": self.usageState,
+            "utilization": self.utilization,
+        }
index d9b119d..e8ca9cf 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 A Class representing an O-RAN radio unit (ORanRu)
 """
 import xml.etree.ElementTree as ET
-from typing import overload
+from typing import Any, cast
 
 from network_generation.model.python.nr_cell_du import NrCellDu
 from network_generation.model.python.o_ran_du import ORanDu
-from network_generation.model.python.o_ran_node import ORanNode
-from network_generation.model.python.o_ran_object import IORanObject
+from network_generation.model.python.o_ran_node import IORanNode, ORanNode
 from network_generation.model.python.o_ran_termination_point import (
     ORanTerminationPoint,
 )
 
 
 # Define the "IORanRu" interface
-class IORanRu(IORanObject):
-    def __init__(
-        self, cell_count: int, ru_angle: int, ru_azimuth: int, **kwargs
-    ):
-        super().__init__(**kwargs)
-        self._cell_count = cell_count
-        self._ru_angle = ru_angle
-        self._ru_azimuth = ru_azimuth
+class IORanRu(IORanNode):
+    cellCount: int
+    ruAngle: int
+    ruAzimuth: int
+
+
+default_value: IORanRu = cast(
+    IORanRu,
+    {
+        **ORanNode.default(),
+        **{"cellCount": 1, "ruAngle": 120, "ruAzimuth": 0},
+    },
+)
 
 
 # Define an abstract O-RAN Node class
-class ORanRu(ORanNode, IORanRu):
-    def __init__(self, o_ran_ru_data: IORanRu = None, **kwargs):
-        super().__init__(o_ran_ru_data, **kwargs)
-        self._cell_count = (
-            o_ran_ru_data["cellCount"]
+class ORanRu(ORanNode):
+    def __init__(
+        self,
+        data: dict[str, Any] = cast(dict[str, Any], default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        o_ran_ru_data: IORanRu = self._to_o_ran_ru_data(data)
+        super().__init__(cast(dict[str, Any], o_ran_ru_data), **kwargs)
+        self._cell_count: int = (
+            int(str(o_ran_ru_data["cellCount"]))
             if o_ran_ru_data and "cellCount" in o_ran_ru_data
             else 1
         )
-        self._ru_angle = (
-            o_ran_ru_data["ruAngle"]
+        self._ru_angle: int = (
+            int(str(o_ran_ru_data["ruAngle"]))
             if o_ran_ru_data and "ruAngle" in o_ran_ru_data
             else 120
         )
-        self._ru_azimuth = (
-            o_ran_ru_data["ruAzimuth"]
+        self._ru_azimuth: int = (
+            int(str(o_ran_ru_data["ruAzimuth"]))
             if o_ran_ru_data and "ruAzimuth" in o_ran_ru_data
             else 0
         )
         self._cells: list[NrCellDu] = self._create_cells()
         name: str = self.name.replace("RU", "DU")
-        self._oRanDu: ORanDu = ORanDu(
-            {
-                "name": name,
-                "geoLocation": self.parent.geoLocation,
-                "position": self.parent.position,
-                "layout": self.layout,
-                "parent": self.parent.parent.parent,
-            }
-        )
+
+        o_ran_du_data: dict[str, Any] = {
+            "name": name,
+            "geoLocation": self.parent.geo_location,
+            "position": self.parent.position,
+            "layout": self.layout,
+            "parent": self.parent.parent.parent,
+        }
+        self._oRanDu: ORanDu = ORanDu(o_ran_du_data)
+
+    def _to_o_ran_ru_data(self, data: dict[str, Any]) -> IORanRu:
+        result: IORanRu = default_value
+        for key, key_type in IORanRu.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
 
     def _create_cells(self) -> list[NrCellDu]:
         result: list[NrCellDu] = []
         cell_angle: int = (
-            self.parent.parent.parent.parent.parent.parent.configuration()[
+            self.parent.parent.parent.parent.parent.parent.configuration[
                 "pattern"
             ]["nr-cell-du"]["cell-angle"]
         )
         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.replace("RU", "NRCellDu"), s[len(s) - 2: len(s)]]
             )
             azimuth: int = index * cell_angle + self._ru_azimuth
             result.append(
                 NrCellDu(
                     {
                         "name": name,
-                        "geoLocation": self.geoLocation,
+                        "geoLocation": self.geo_location,
                         "position": self.position,
                         "layout": self.layout,
-                        "spiralRadiusProfile": self.spiralRadiusProfile,
                         "parent": self,
                         "cellAngle": cell_angle,
                         "azimuth": azimuth,
@@ -108,9 +123,8 @@ class ORanRu(ORanNode, IORanRu):
     def oRanDu(self) -> ORanDu:
         return self._oRanDu
 
-    @property
     def termination_points(self) -> list[ORanTerminationPoint]:
-        result: list[ORanTerminationPoint] = super().termination_points
+        result: list[ORanTerminationPoint] = super().termination_points()
         phy_tp: str = "-".join([self.name, "phy".upper()])
         result.append(ORanTerminationPoint({"id": phy_tp, "name": phy_tp}))
         for interface in ["ofhm", "ofhc", "ofhu", "ofhs"]:
@@ -121,16 +135,16 @@ class ORanRu(ORanNode, IORanRu):
                 )
             )
         for cell in self.cells:
-            result.extend(cell.termination_points)
+            result.extend(cell.termination_points())
         return result
 
-    def to_topology_nodes(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_nodes()
+    def to_topology_nodes(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_nodes()
         result.extend(self.oRanDu.to_topology_nodes())
         return result
 
-    def to_topology_links(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_links()
+    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(
@@ -163,5 +177,5 @@ class ORanRu(ORanNode, IORanRu):
             o_ran_ru.append(cell.toKml())
         return o_ran_ru
 
-    def toSvg(self) -> None:
-        return None
+    def toSvg(self) -> ET.Element:
+        return ET.Element("to-be-implemented")
index 9b1be83..9308291 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
-A Class representing an O-RAN Service Management and Orchestration Framework (SMO)
+A Class representing an O-RAN Service Management and
+Orchestration Framework (SMO)
 """
 import xml.etree.ElementTree as ET
-from typing import overload
+from typing import Any, cast
 
 import network_generation.model.python.hexagon as Hexagon
+from network_generation.model.python.geo_location import GeoLocation
 from network_generation.model.python.hexagon import Hex
 from network_generation.model.python.o_ran_near_rt_ric import ORanNearRtRic
-from network_generation.model.python.o_ran_node import ORanNode
-from network_generation.model.python.o_ran_object import IORanObject
+from network_generation.model.python.o_ran_node import (
+    IORanNode,
+    ORanNode,
+    default_value,
+)
 from network_generation.model.python.o_ran_termination_point import (
     ORanTerminationPoint,
 )
 from network_generation.model.python.tower import Tower
 
-
 # Define the "IORanSmo" interface
-class IORanSmo(IORanObject):
-    def __init__(self, **kwargs):
-        super().__init__(**kwargs)
+IORanSmo = IORanNode
 
 
 # Define an abstract O-RAN Node class
-class ORanSmo(ORanNode, IORanSmo):
-    def __init__(self, o_ran_smo_data: IORanSmo = None, **kwargs):
-        super().__init__(o_ran_smo_data, **kwargs)
-        self._o_ran_near_rt_rics: list[
-            ORanNearRtRic
-        ] = self._calculate_near_rt_rics()
+class ORanSmo(ORanNode):
+    """
+    Class representing an O-RAN Service Management and Operation object.
+    """
+
+    def __init__(
+        self,
+        data: dict[str, Any] = cast(dict[str, Any], default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        o_ran_smo_data: IORanSmo = self._to_o_ran_smo_data(data)
+        super().__init__(cast(dict[str, Any], o_ran_smo_data), **kwargs)
+        if self.parent is not None:
+            self._o_ran_near_rt_rics: list[
+                ORanNearRtRic
+            ] = self._calculate_near_rt_rics()
 
     def _calculate_near_rt_rics(self) -> list[ORanNearRtRic]:
         hex_ring_radius: int = (
-            self.spiralRadiusProfile.oRanSmoSpiralRadiusOfNearRtRics
+            self.parent.spiral_radius_profile.oRanSmoSpiralRadiusOfNearRtRics
+            if self.parent is not None
+            else 1
         )
-        hex_list: list[Hex] = self.spiralRadiusProfile.oRanNearRtRicSpiral(
+        hex_list: list[
+            Hex
+        ] = self.parent.spiral_radius_profile.oRanNearRtRicSpiral(
             self.position, hex_ring_radius
         )
         result: list[ORanNearRtRic] = []
         for index, hex in enumerate(hex_list):
             s: str = "00" + str(index)
             name: str = "-".join(
-                [self.name.replace("SMO", "NearRtRic"), s[len(s) - 2 : len(s)]]
+                [self.name.replace("SMO", "NearRtRic"), s[len(s) - 2: len(s)]]
             )
-            network_center: dict = self.parent.center
+            network_center: GeoLocation = self.parent.center
             newGeo = Hexagon.hex_to_geo_location(
                 self.layout, hex, network_center
             ).json()
@@ -69,47 +85,58 @@ class ORanSmo(ORanNode, IORanSmo):
                         "geoLocation": newGeo,
                         "position": hex,
                         "layout": self.layout,
-                        "spiralRadiusProfile": self.spiralRadiusProfile,
                         "parent": self,
-                    }
+                    },
                 )
             )
         return result
 
+    def _to_o_ran_smo_data(self, data: dict[str, Any]) -> IORanSmo:
+        result: IORanSmo = default_value
+        for key, key_type in IORanSmo.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
+
     @property
     def o_ran_near_rt_rics(self) -> list[ORanNearRtRic]:
         return self._o_ran_near_rt_rics
 
     @property
+    def towers(self) -> list[Tower]:
+        result: list[Tower] = []
+        for ric in self.o_ran_near_rt_rics:
+            for tower in ric.towers:
+                result.append(tower)
+        return result
+
+    # @property
     def termination_points(self) -> list[ORanTerminationPoint]:
-        result: list[ORanTerminationPoint] = super().termination_points
+        result: list[ORanTerminationPoint] = super().termination_points()
         phy_tp: str = "-".join([self.name, "phy".upper()])
         result.append(ORanTerminationPoint({"id": phy_tp, "name": phy_tp}))
         for interface in ["a1", "o1", "o2"]:
             id: str = "-".join([self.name, interface.upper()])
             result.append(
                 ORanTerminationPoint(
-                    {"id": id, "name": id, "supporter": phy_tp, "parent": self}
+                    {
+                        "id": id,
+                        "name": id,
+                        "supporter": phy_tp,
+                        "parent": self,
+                    },
                 )
             )
         return result
 
-    @property
-    def towers(self) -> list[Tower]:
-        result: list[Tower] = []
-        for ric in self.o_ran_near_rt_rics:
-            for tower in ric.towers:
-                result.append(tower)
-        return result
-
-    def to_topology_nodes(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_nodes()
+    def to_topology_nodes(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_nodes()
         for ric in self.o_ran_near_rt_rics:
             result.extend(ric.to_topology_nodes())
         return result
 
-    def to_topology_links(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = []  # super().to_topology_links()
+    def to_topology_links(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = []  # super().to_topology_links()
         for ric in self.o_ran_near_rt_rics:
             result.extend(ric.to_topology_links())
         return result
@@ -124,5 +151,5 @@ class ORanSmo(ORanNode, IORanSmo):
             smo.append(ric.toKml())
         return smo
 
-    def toSvg(self) -> None:
-        return None
+    def toSvg(self) -> ET.Element:
+        return ET.Element("not-implemented-yet-TODO")
index fd8c289..75d684c 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
+
+from typing import Any
 
 import network_generation.model.python.hexagon as Hexagon
-from network_generation.model.python.cube import Cube
 from network_generation.model.python.hexagon import Hex
 
 
 class SpiralRadiusProfile:
-    def __init__(self, data: [dict[str, dict] | None] = None):
+    def __init__(self, data: dict[str, Any] | None = None) -> None:
         self._oRanSmoSpiralRadiusOfNearRtRics = (
             data.get("oRanSmoSpiralRadiusOfNearRtRics", 1) if data else 1
         )
@@ -33,10 +34,20 @@ class SpiralRadiusProfile:
         self._oRanDuSpiralRadiusOfTowers = (
             data.get("oRanDuSpiralRadiusOfTowers", 1) if data else 1
         )
+        # self._nrDuCellsPerSector: int = (
+        #     int(str(data.get("sectorCount"))) if data else 1
+        # )
 
     @property
     def id(self) -> str:
-        return f"{self._oRanDuSpiralRadiusOfTowers}{self._oRanCuSpiralRadiusOfODus}{self._oRanNearRtRicSpiralRadiusOfOCus}{self._oRanSmoSpiralRadiusOfNearRtRics}"
+        return "".join(
+            [
+                str(self._oRanDuSpiralRadiusOfTowers),
+                str(self._oRanCuSpiralRadiusOfODus),
+                str(self._oRanNearRtRicSpiralRadiusOfOCus),
+                str(self._oRanSmoSpiralRadiusOfNearRtRics),
+            ]
+        )
 
     @property
     def count(self) -> int:
@@ -64,7 +75,7 @@ class SpiralRadiusProfile:
         return self._oRanSmoSpiralRadiusOfNearRtRics
 
     @oRanSmoSpiralRadiusOfNearRtRics.setter
-    def oRanSmoSpiralRadiusOfNearRtRics(self, value: int):
+    def oRanSmoSpiralRadiusOfNearRtRics(self, value: int) -> None:
         self._oRanSmoSpiralRadiusOfNearRtRics = value
 
     @property
@@ -72,7 +83,7 @@ class SpiralRadiusProfile:
         return self._oRanNearRtRicSpiralRadiusOfOCus
 
     @oRanNearRtRicSpiralRadiusOfOCus.setter
-    def oRanNearRtRicSpiralRadiusOfOCus(self, value: int):
+    def oRanNearRtRicSpiralRadiusOfOCus(self, value: int) -> None:
         self._oRanNearRtRicSpiralRadiusOfOCus = value
 
     @property
@@ -80,7 +91,7 @@ class SpiralRadiusProfile:
         return self._oRanCuSpiralRadiusOfODus
 
     @oRanCuSpiralRadiusOfODus.setter
-    def oRanCuSpiralRadiusOfODus(self, value: int):
+    def oRanCuSpiralRadiusOfODus(self, value: int) -> None:
         self._oRanCuSpiralRadiusOfODus = value
 
     @property
@@ -88,7 +99,7 @@ class SpiralRadiusProfile:
         return self._oRanDuSpiralRadiusOfTowers
 
     @oRanDuSpiralRadiusOfTowers.setter
-    def oRanDuSpiralRadiusOfTowers(self, value: int):
+    def oRanDuSpiralRadiusOfTowers(self, value: int) -> None:
         self._oRanDuSpiralRadiusOfTowers = value
 
     @property
@@ -96,7 +107,7 @@ class SpiralRadiusProfile:
         return self._sectors
 
     @sectors.setter
-    def sectors(self, value: int):
+    def sectors(self, value: int) -> None:
         self._sectors = value
 
     @property
@@ -104,8 +115,8 @@ class SpiralRadiusProfile:
         return self._nrDuCellsPerSector
 
     @nrDuCellsPerSector.setter
-    def nrDuCellsPerSector(self, value: int):
-        self._nrDuCellsPerSector: int = value
+    def nrDuCellsPerSector(self, value: int) -> None:
+        self._nrDuCellsPerSector = value
 
     def oRanDuDirections(self) -> list[Hex]:
         q: int = 2 * self._oRanDuSpiralRadiusOfTowers + 1
@@ -120,13 +131,14 @@ class SpiralRadiusProfile:
             Hex(-r, -s, -q),
         ]
 
-    def oRanDuNeighbor(self, cube: Cube, direction: int):
+    def oRanDuNeighbor(self, cube: Hex, direction: int) -> Hex:
         return Hexagon.hex_add(cube, self.oRanDuDirections()[direction])
 
     def oRanDuRing(self, center: Hex, radius: int) -> list[Hex]:
         if radius <= 0:
             raise ValueError(
-                "Invalid radius. The radius around the hex center must be greater than 0 rings."
+                "Invalid radius. The radius around the hex center "
+                + "must be greater than 0 rings."
             )
         results: list[Hex] = []
         hex: Hex = Hexagon.hex_add(
@@ -166,13 +178,14 @@ class SpiralRadiusProfile:
             Hex(-r, -s, -q),
         ]
 
-    def oRanCuNeighbor(self, cube: Hex, direction: int) -> list[Hex]:
+    def oRanCuNeighbor(self, cube: Hex, direction: int) -> Hex:
         return Hexagon.hex_add(cube, self.oRanCuDirections()[direction])
 
-    def oRanCuRing(self, center: Hex, radius: int):
+    def oRanCuRing(self, center: Hex, radius: int) -> list[Hex]:
         if not (radius > 0):
             raise ValueError(
-                "Invalid radius. The radius around the hex center must be greater than 0 rings."
+                "Invalid radius. The radius around the hex center "
+                + "must be greater than 0 rings."
             )
 
         results: list[Hex] = []
@@ -207,45 +220,45 @@ class SpiralRadiusProfile:
         q: int = 3 * q0 - self.oRanNearRtRicSpiralRadiusOfOCus
         r: int = -r0 - self.oRanNearRtRicSpiralRadiusOfOCus
 
-        profile_id: str = self.id[0 : len(self.id) - 1]
+        profile_id: str = self.id[0: len(self.id) - 1]
         if profile_id in {"111", "112", "113", "114"}:
-            q: int = 21 + 14 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = -7 * self.oRanNearRtRicSpiralRadiusOfOCus
+            q = 21 + 14 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = -7 * self.oRanNearRtRicSpiralRadiusOfOCus
         elif profile_id in {"121", "122", "123", "124"}:
-            q: int = 25 + 13 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = 9 + 10 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            q = 25 + 13 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = 9 + 10 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
         elif profile_id in {"131", "132"}:
-            q: int = 49 + 30 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = -21 - 34 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            q = 49 + 30 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = -21 - 34 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
         elif profile_id == "133":
-            q: int = 74
-            r: int = 37
+            q = 74
+            r = 37
         elif profile_id == "134":
-            q: int = 93
-            r: int = 50
+            q = 93
+            r = 50
         elif profile_id in {"211", "212", "213", "214"}:
-            q: int = 34 + 23 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = -10 * self.oRanNearRtRicSpiralRadiusOfOCus - 1
+            q = 34 + 23 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = -10 * self.oRanNearRtRicSpiralRadiusOfOCus - 1
         elif profile_id in {"221", "222", "223", "224"}:
-            q: int = 57 + 38 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = -19 * self.oRanNearRtRicSpiralRadiusOfOCus
+            q = 57 + 38 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = -19 * self.oRanNearRtRicSpiralRadiusOfOCus
         elif profile_id in {"231", "232", "233", "234"}:
-            q: int = 80 + 53 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = -28 * self.oRanNearRtRicSpiralRadiusOfOCus - 1
+            q = 80 + 53 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = -28 * self.oRanNearRtRicSpiralRadiusOfOCus - 1
         elif profile_id in {"241", "242", "243", "244"}:
-            q: int = 103 + 68 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = -39 * self.oRanNearRtRicSpiralRadiusOfOCus + 2 * (
+            q = 103 + 68 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = -39 * self.oRanNearRtRicSpiralRadiusOfOCus + 2 * (
                 self.oRanNearRtRicSpiralRadiusOfOCus - 1
             )
         elif profile_id in {"311", "312", "313", "314"}:
-            q: int = 47 + 32 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = -11 - 13 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            q = 47 + 32 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = -11 - 13 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
         elif profile_id in {"321", "322", "323", "324"}:
-            q: int = 79 + 53 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = -24 - 25 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            q = 79 + 53 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = -24 - 25 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
         elif profile_id in {"331", "332", "333", "334"}:
-            q: int = 111 + 75 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
-            r: int = -37 - 37 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            q = 111 + 75 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
+            r = -37 - 37 * (self.oRanNearRtRicSpiralRadiusOfOCus - 1)
         else:
             # Handle the default case or raise a warning
             pass
@@ -260,13 +273,14 @@ class SpiralRadiusProfile:
             Hex(-r, -s, -q),
         ]
 
-    def oRanNearRtRicNeighbor(self, cube: Hex, direction: int):
+    def oRanNearRtRicNeighbor(self, cube: Hex, direction: int) -> Hex:
         return Hexagon.hex_add(cube, self.oRanNearRtRicDirections()[direction])
 
     def oRanNearRtRicRing(self, center: Hex, radius: int) -> list[Hex]:
         if not (radius > 0):
             raise ValueError(
-                "Invalid radius. The radius around the hex center must be greater than 0 rings."
+                "Invalid radius. The radius around the hex center "
+                + "must be greater than 0 rings."
             )
 
         results: list[Hex] = []
index 6aba16d..034c49a 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 An abstract Class for O-RAN TerminationPoint
 """
-from abc import abstractmethod
+from typing import Any, cast
 
 from network_generation.model.python.o_ran_object import (
     IORanObject,
@@ -26,50 +26,94 @@ from network_generation.model.python.o_ran_object import (
 
 
 # Define the "IORanObject" interface
-class IORanTerminationPointData(IORanObject):
-    def __init__(self, supporter: str = None, parent=None, **kwargs):
-        super().__init__(**kwargs)
-        self.supporter = supporter
-        self.parent = parent
-
-
-# Define an O-RAN Termination Point (ietf-interface, onf:logical-termination-point) class
-class ORanTerminationPoint(ORanObject, IORanTerminationPointData):
-    def __init__(self, tp: IORanTerminationPointData = None, **kwargs):
-        super().__init__(tp, **kwargs)
-        self.supporter = tp["supporter"] if tp and "supporter" in tp else None
-        self.parent = tp["parent"] if tp and "parent" in tp else None
-
-    def to_topology(self):
-        result: dict[str, dict] = {"tp-id": self.name}
-        if self.supporter:
-            network_ref: str = ""
-            match str(type(self.parent)):
-                case "<class 'model.python.o_ran_smo.ORanSmo'>":
-                    network_ref = self.parent.parent.id
-                case "<class 'model.python.o_ran_near_rt_ric.ORanNearRtRic'>":
-                    network_ref = self.parent.parent.parent.id
-                case "<class 'model.python.o_ran_cu.ORanCu'>":
-                    network_ref = self.parent.parent.parent.parent.id
-                case "<class 'model.python.o_ran_du.ORanDu'>":
-                    network_ref = self.parent.parent.parent.parent.parent.id
-                case "<class 'model.python.o_ran_cloud_du.ORanCloudDu'>":
-                    network_ref = self.parent.parent.parent.parent.parent.id
-                case "<class 'model.python.o_ran_ru.ORanRu'>":
-                    network_ref = (
-                        self.parent.parent.parent.parent.parent.parent.id
-                    )
-                case _:
-                    print("unknown: implement " + str(type(self.parent)))
-                    network_ref = "unknown: implement " + str(
-                        type(self.parent)
-                    )
-
-            result["supporting-termination-point"] = [
-                {
-                    "network-ref": network_ref,
-                    "node-ref": self.parent.name,
-                    "tp-ref": self.supporter,
-                }
-            ]
+class IORanTerminationPoint(IORanObject):
+    supporter: str
+    parent: Any
+
+
+default_value: IORanTerminationPoint = cast(
+    IORanTerminationPoint,
+    {
+        **ORanObject.default(),
+        **{
+            "supporter": "TerminationPointLayer",
+            "parent": None,
+        },
+    },
+)
+
+
+# Define an O-RAN Termination Point
+# (ietf-interface, onf:logical-termination-point) class
+class ORanTerminationPoint(ORanObject):
+    @staticmethod
+    def default() -> dict[str, Any]:
+        return cast(dict[str, Any], default_value)
+
+    def __init__(
+        self, data: dict[str, Any] = default(), **kwargs: dict[str, Any]
+    ) -> None:
+        itp: IORanTerminationPoint = self._to_itp_data(data)
+        super().__init__(cast(dict[str, Any], itp), **kwargs)
+        self._supporter: str = str(itp["supporter"])
+        self._parent: Any = itp["parent"]
+
+    def _to_itp_data(self, data: dict[str, Any]) -> IORanTerminationPoint:
+        result: IORanTerminationPoint = default_value
+        for key, key_type in IORanTerminationPoint.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
+
+    @property
+    def supporter(self) -> str:
+        return self._supporter
+
+    @supporter.setter
+    def supporter(self, value: str) -> None:
+        self._supporter = value
+
+    @property
+    def parent(self) -> Any:
+        return self._utilization
+
+    @parent.setter
+    def parent(self, value: Any) -> None:
+        self._parent = value
+
+    def to_topology(self) -> dict[str, Any]:
+        result: dict[str, Any] = {"tp-id": self.name}
+        # TODO
+        # if self.supporter:
+        #     network_ref: str = ""
+        #     match self.parent.__qualname__:
+        #         case ORanSmo.__qualname__:
+        #             network_ref = self.parent.parent.id
+        # case "<class 'model.python.o_ran_smo.ORanSmo'>":
+        #     network_ref = self.parent.parent.id
+        # case "<class 'model.python.o_ran_near_rt_ric.ORanNearRtRic'>":
+        #     network_ref = self.parent.parent.parent.id
+        # case "<class 'model.python.o_ran_cu.ORanCu'>":
+        #     network_ref = self.parent.parent.parent.parent.id
+        # case "<class 'model.python.o_ran_du.ORanDu'>":
+        #     network_ref = self.parent.parent.parent.parent.parent.id
+        # case "<class 'model.python.o_ran_cloud_du.ORanCloudDu'>":
+        #     network_ref = self.parent.parent.parent.parent.parent.id
+        # case "<class 'model.python.o_ran_ru.ORanRu'>":
+        #     network_ref = (
+        #         self.parent.parent.parent.parent.parent.parent.id
+        #     )
+        #     case _:
+        #         print("unknown: implement " + str(type(self.parent)))
+        #         network_ref = "unknown: implement " + str(
+        #             type(self.parent)
+        #         )
+
+        # result["supporting-termination-point"] = [
+        #     {
+        #         "network-ref": network_ref,
+        #         "node-ref": self.parent.name,
+        #         "tp-ref": self.supporter,
+        #     }
+        # ]
         return result
index 1a9b772..41cd8a0 100644 (file)
@@ -14,7 +14,7 @@
 #
 # inspired by http://www.redblobgames.com/grids/hexagons/
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 from __future__ import division, print_function
 
index 5d6ac02..e68c615 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 An abstract Class for all classes
 """
-import uuid
-from abc import ABC
+from abc import ABC, abstractmethod
+from typing import Any, TypedDict, cast
 
 from network_generation.model.python.type_definitions import (
     AdministrativeState,
@@ -31,70 +31,65 @@ from network_generation.model.python.type_definitions import (
 
 
 # Define the ITop interface
-class ITop:
-    def __init__(
-        self,
-        id: str = None,
-        name: str = None,
-        administrativeState: AdministrativeState = None,
-        operationalState: OperationalState = None,
-        lifeCycleState: LifeCycleState = None,
-        alarmState: AlarmState = None,
-        usageState: UsageState = None,
-        utilization: Utilization = None,
-    ):
-        self.id = id
-        self.name = name
-        self.administrativeState = administrativeState
-        self.operationalState = operationalState
-        self.lifeCycleState = lifeCycleState
-        self.alarmState = alarmState
-        self.usageState = usageState
-        self.utilization = utilization
+class ITop(TypedDict):
+    id: str
+    name: str
+    administrativeState: AdministrativeState
+    operationalState: OperationalState
+    lifeCycleState: LifeCycleState
+    alarmState: AlarmState
+    usageState: UsageState
+    utilization: Utilization
+
+
+# define default value
+default_value: ITop = {
+    "id": "be5229af-2660-4bae-8f2c-b9d0f788fad1",
+    "name": "NoName",
+    "administrativeState": AdministrativeState.LOCKED,
+    "operationalState": OperationalState.DISABLED,
+    "lifeCycleState": LifeCycleState.PLANNED,
+    "alarmState": 0,
+    "usageState": UsageState.UNUSED,
+    "utilization": 0,
+}
 
 
 # Define the Top class
-class Top(ABC, ITop):
-    def __init__(self, data: [dict[str, dict] | None] = None):
-        self._id = data["id"] if data and "id" in data else str(uuid.uuid4())
-        self._name = (
-            data["name"]
-            if data and "name" in data
-            else " ".join(["Name", "of", self._id])
-        )
-        self._administrativeState = (
-            data["administrativeState"]
-            if data and "administrativeState" in data
-            else AdministrativeState.LOCKED
-        )
-        self._operationalState = (
-            data["operationalState"]
-            if data and "operationalState" in data
-            else OperationalState.DISABLED
-        )
-        self._lifeCycleState = (
-            data["lifeCycleState"]
-            if data and "lifeCycleState" in data
-            else LifeCycleState.PLANNED
-        )
-        self._alarmState = (
-            data["alarmState"] if data and "alarmState" in data else 0
-        )
-        self._usageState = (
-            data["usageState"]
-            if data and "usageState" in data
-            else UsageState.UNUSED
-        )
-        self._utilization = (
-            data["utilization"] if data and "utilization" in data else 0
-        )
+class Top(ABC):
+    @staticmethod
+    def default() -> dict[str, Any]:
+        return cast(dict[str, Any], default_value)
+
+    def __init__(
+        self, data: dict[str, Any] = cast(dict[str, Any], default_value)
+    ) -> None:
+        super().__init__()
+        itop: ITop = self._to_itop_data(data)
+        self._id: str = itop["id"]
+        self._name: str = itop["name"]
+        self._administrativeState: AdministrativeState = itop[
+            "administrativeState"
+        ]
+        self._operationalState: OperationalState = itop["operationalState"]
+        self._lifeCycleState: LifeCycleState = itop["lifeCycleState"]
+        self._alarmState: AlarmState = itop["alarmState"]
+        self._usageState: UsageState = itop["usageState"]
+        self._utilization: Utilization = itop["utilization"]
+
+    def _to_itop_data(self, data: dict[str, Any]) -> ITop:
+        result: ITop = default_value
+        for key, key_type in ITop.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
 
     @property
     def id(self) -> str:
         return self._id
 
     @id.setter
-    def id(self, value: str):
+    def id(self, value: str) -> None:
         self._id = value
 
     @property
@@ -102,7 +97,7 @@ class Top(ABC, ITop):
         return self._name
 
     @name.setter
-    def name(self, value: str):
+    def name(self, value: str) -> None:
         self._name = value
 
     @property
@@ -110,7 +105,7 @@ class Top(ABC, ITop):
         return self._administrativeState
 
     @administrativeState.setter
-    def administrativeState(self, value: AdministrativeState):
+    def administrativeState(self, value: AdministrativeState) -> None:
         self._administrativeState = value
 
     @property
@@ -118,7 +113,7 @@ class Top(ABC, ITop):
         return self._operationalState
 
     @operationalState.setter
-    def operationalState(self, value: OperationalState):
+    def operationalState(self, value: OperationalState) -> None:
         self._operationalState = value
 
     @property
@@ -126,7 +121,7 @@ class Top(ABC, ITop):
         return self._lifeCycleState
 
     @lifeCycleState.setter
-    def lifeCycleState(self, value: LifeCycleState):
+    def lifeCycleState(self, value: LifeCycleState) -> None:
         self._lifeCycleState = value
 
     @property
@@ -134,7 +129,7 @@ class Top(ABC, ITop):
         return self._alarmState
 
     @alarmState.setter
-    def alarmState(self, value: AlarmState):
+    def alarmState(self, value: AlarmState) -> None:
         self._alarmState = value
 
     @property
@@ -142,7 +137,7 @@ class Top(ABC, ITop):
         return self._usageState
 
     @usageState.setter
-    def usageState(self, value: UsageState):
+    def usageState(self, value: UsageState) -> None:
         self._usageState = value
 
     @property
@@ -150,18 +145,19 @@ class Top(ABC, ITop):
         return self._utilization
 
     @utilization.setter
-    def utilization(self, value: Utilization):
+    def utilization(self, value: Utilization) -> None:
         self._utilization = value
 
-    def json(self) -> dict[str, dict]:
+    @abstractmethod
+    def json(self) -> dict[str, Any]:
         return {
             "id": self.id,
             "name": self.name,
-            "administrativeState": self.administrativeState.value,
-            "operationalState": self.operationalState.value,
-            "lifeCycleState": self.lifeCycleState.value,
+            "administrativeState": self.administrativeState,
+            "operationalState": self.operationalState,
+            "lifeCycleState": self.lifeCycleState,
             "alarmState": self.alarmState,
-            "usageState": self.usageState.value,
+            "usageState": self.usageState,
             "utilization": self.utilization,
         }
 
index d708d5e..d650ee0 100644 (file)
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 A Class representing a Tower to mount O-RAN RUs
@@ -20,10 +20,9 @@ It can be interpreted as 'resource pool' for physical network
 functions.
 """
 import xml.etree.ElementTree as ET
-from typing import overload
+from typing import Any, cast
 
-from network_generation.model.python.o_ran_node import ORanNode
-from network_generation.model.python.o_ran_object import IORanObject
+from network_generation.model.python.o_ran_node import IORanNode, ORanNode
 from network_generation.model.python.o_ran_ru import ORanRu
 from network_generation.model.python.o_ran_termination_point import (
     ORanTerminationPoint,
@@ -31,37 +30,58 @@ from network_generation.model.python.o_ran_termination_point import (
 
 
 # Define the "IORanDu" interface
-class ITower(IORanObject):
-    def __init__(self, o_ran_ru_count: int, **kwargs):
-        super().__init__(**kwargs)
-        self._o_ran_ru_count = o_ran_ru_count
+class ITower(IORanNode):
+    o_ran_ru_count: int
+
+
+default_value: ITower = cast(
+    ITower,
+    {
+        **ORanNode.default(),
+        **{
+            "o_ran_ru_count": 1,
+        },
+    },
+)
 
 
 # Implement a concrete O-RAN Node class
 class Tower(ORanNode):
-    def __init__(self, tower_data: ITower = None, **kwargs):
-        super().__init__(tower_data, **kwargs)
-        self._o_ran_ru_count = (
-            tower_data["oRanRuCount"]
+    def __init__(
+        self,
+        data: dict[str, Any] = cast(dict[str, Any], default_value),
+        **kwargs: dict[str, Any]
+    ) -> None:
+        tower_data: ITower = self._to_tower_data(data)
+        super().__init__(cast(dict[str, Any], tower_data), **kwargs)
+        self._o_ran_ru_count: int = (
+            int(str(tower_data["oRanRuCount"]))
             if tower_data and "oRanRuCount" in tower_data
             else 3
         )
         self._o_ran_rus: list[ORanRu] = self._create_o_ran_rus()
 
+    def _to_tower_data(self, data: dict[str, Any]) -> ITower:
+        result: ITower = default_value
+        for key, key_type in ITower.__annotations__.items():
+            if key in data:
+                result[key] = data[key]  # type: ignore
+        return result
+
     def _create_o_ran_rus(self) -> list[ORanRu]:
         result: list[ORanRu] = []
         for index in range(self._o_ran_ru_count):
             s: str = "00" + str(index)
             name: str = "-".join(
-                [self.name.replace("Tower", "RU"), s[len(s) - 2 : len(s)]]
+                [self.name.replace("Tower", "RU"), s[len(s) - 2: len(s)]]
             )
             cell_count: int = (
-                self.parent.parent.parent.parent.parent.configuration()[
+                self.parent.parent.parent.parent.parent.configuration[
                     "pattern"
                 ]["o-ran-ru"]["nr-cell-du-count"]
             )
             cell_angle: int = (
-                self.parent.parent.parent.parent.parent.configuration()[
+                self.parent.parent.parent.parent.parent.configuration[
                     "pattern"
                 ]["nr-cell-du"]["cell-angle"]
             )
@@ -71,10 +91,9 @@ class Tower(ORanNode):
                 ORanRu(
                     {
                         "name": name,
-                        "geoLocation": self.geoLocation,
+                        "geoLocation": self.geo_location,
                         "position": self.position,
                         "layout": self.layout,
-                        "spiralRadiusProfile": self.spiralRadiusProfile,
                         "parent": self,
                         "cellCount": cell_count,
                         "ruAngle": ru_angle,
@@ -88,36 +107,37 @@ class Tower(ORanNode):
     def o_ran_rus(self) -> list[ORanRu]:
         return self._o_ran_rus
 
-    @property
     def termination_points(self) -> list[ORanTerminationPoint]:
-        result: list[ORanTerminationPoint] = super().termination_points
+        result: list[ORanTerminationPoint] = super().termination_points()
         phy_tp: str = "-".join([self.name, "phy".upper()])
-        result.append({"tp-id": phy_tp})
+        result.append(ORanTerminationPoint({"tp-id": phy_tp}))
         for interface in ["e2", "o1", "ofhm", "ofhc", "ofhu", "ofhs"]:
             result.append(
-                {
-                    "tp-id": "-".join([self.name, interface.upper()]),
-                    "supporting-termination-point": [
-                        {
-                            "network-ref": type(
-                                self.parent.parent.parent.parent
-                            ),
-                            "node-ref": self.name,
-                            "tp-ref": phy_tp,
-                        }
-                    ],
-                }
+                ORanTerminationPoint(
+                    {
+                        "tp-id": "-".join([self.name, interface.upper()]),
+                        "supporting-termination-point": [
+                            {
+                                "network-ref": type(
+                                    self.parent.parent.parent.parent
+                                ),
+                                "node-ref": self.name,
+                                "tp-ref": phy_tp,
+                            }
+                        ],
+                    }
+                )
             )
         return result
 
-    def to_topology_nodes(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_nodes()
+    def to_topology_nodes(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_nodes()
         for o_ran_ru in self.o_ran_rus:
             result.extend(o_ran_ru.to_topology_nodes())
         return result
 
-    def to_topology_links(self) -> list[dict[str, dict]]:
-        result: list[dict[str, dict]] = super().to_topology_links()
+    def to_topology_links(self) -> list[dict[str, Any]]:
+        result: list[dict[str, Any]] = super().to_topology_links()
         for o_ran_ru in self.o_ran_rus:
             result.extend(o_ran_ru.to_topology_links())
         return result
@@ -132,5 +152,5 @@ class Tower(ORanNode):
             tower.append(o_ran_ru.toKml())
         return tower
 
-    def toSvg(self) -> None:
-        return None
+    def toSvg(self) -> ET.Element:
+        return ET.Element("to-be-implemented")
index 1430c3c..9dc0c8a 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 
 """
 A collection of TypeDefinitions
 """
 from enum import Enum
+from typing import TypedDict
 
 from network_generation.model.python.countries import Country
 
+# from typing import Any, TypedDict, TypeVar
+
+
+# from network_generation.model.python.o_ran_node import IORanNode
+# from network_generation.model.python.o_ran_object import IORanObject
+# from network_generation.model.python.top import ITop
+
+# Generic Types based on inheritance
+# IORanType = TypeVar("IORanType", ITop, IORanObject, IORanNode)
+
 
 # Define AdministrativeState enum
 class AdministrativeState(Enum):
@@ -32,16 +43,16 @@ class AdministrativeState(Enum):
 # Define AlarmState type
 AlarmState = int
 
+
 # Define Address type
-AddressType = {
-    "street": str,
-    "building": str,
-    "room": str,
-    "city": str,
-    "zip": str,
-    "state": str,
-    "country": Country,
-}
+class AddressType(TypedDict):
+    street: str
+    building: str
+    room: str
+    city: str
+    zip: str
+    state: str
+    country: Country
 
 
 # Define OperationalState enum
@@ -66,20 +77,4 @@ class UsageState(Enum):
     UNUSED = "unused"
 
 
-# Define Enumerate type
-def Enumerate(N, Acc=None):
-    if Acc is None:
-        Acc = []
-    if len(Acc) == N:
-        return Acc[-1]
-    return Enumerate(N, Acc + [len(Acc)])
-
-
-# Define Range type
-def Range(F, T) -> list[int]:
-    return [i for i in range(F, T + 1)]
-
-
-# Define Procent and Utilization types
-Procent = Range(0, 100)
-Utilization = Procent
+Utilization = int
index 2c47299..4c1dabe 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 """
 Module containing a class for parameter validation
 """
 import json
 import os
 import os.path
+from typing import Any
 
 import jsonschema
 
@@ -39,9 +40,8 @@ class ParameterValidator:
     __is_valid: bool = False
 
     # constructor
-    def __init__(self, args):
+    def __init__(self, args: list[str]) -> None:
         self.args = args
-
         if len(self.args) > 1:
             self.__config_file = args[1]
 
@@ -83,15 +83,17 @@ class ParameterValidator:
 
     def error_message(self) -> str:
         """
-        Getter for the error message after validation process or an empty sting,
-        when configuration is valid.
+        Getter for the error message after validation process or an
+        empty sting,  when configuration is valid.
         :return Error message as string.
         """
         return self.__error_message
 
     # private
 
-    def __is_json_valid(self, json_data, json_schema) -> bool:
+    def __is_json_valid(
+        self, json_data: dict[str, Any], json_schema: dict[str, Any]
+    ) -> bool:
         """
         Method validating json against a schema
         """
index 54ba84f..fd8c4fa 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#!/usr/bin/python
+# !/usr/bin/python
 """
 Provides functions to convert the Network into different formats
 """
 
 import json
 import xml.etree.ElementTree as ET
+from typing import Any
 
 from network_generation.model.python.o_ran_network import ORanNetwork
 
 
 class NetworkViewer:
     """
-    This class contains all functions converting the Network into different formats
+    This class contains all functions converting the Network into
+    different formats
     """
 
-    __network: ORanNetwork = None
-
     # constructor
-    def __init__(self, network: ORanNetwork):
+    def __init__(self, network: ORanNetwork) -> None:
         self.__network = network
 
     # json format
@@ -43,7 +43,7 @@ class NetworkViewer:
         """
         return self
 
-    def show_as_json(self) -> dict[str, dict]:
+    def show_as_json(self) -> None:
         """
         Method printing the class in json format.
         """
@@ -62,7 +62,7 @@ class NetworkViewer:
         :type filename: string
         """
         with open(filename, "w", encoding="utf-8") as json_file:
-            output: dict[str, dict] = self.__network.to_topology()
+            output: dict[str, Any] = self.__network.to_topology()
             json.dump(output, json_file, ensure_ascii=False, indent=2)
             print("File '" + filename + "' saved!")
 
@@ -106,12 +106,12 @@ class NetworkViewer:
                 style = ET.Element("Style", {"id": key})
                 line_style = ET.SubElement(style, "LineStyle")
                 color = ET.SubElement(line_style, "color")
-                color.text = value["stroke"]["color"]
+                color.text = str(value["stroke"]["color"])
                 width = ET.SubElement(line_style, "width")
-                width.text = value["stroke"]["width"]
+                width.text = str(value["stroke"]["width"])
                 poly_style = ET.SubElement(style, "PolyStyle")
                 fill = ET.SubElement(poly_style, "color")
-                fill.text = value["fill"]["color"]
+                fill.text = str(value["fill"]["color"])
                 root.findall(".//Document")[0].append(style)
 
         ET.ElementTree(root).write(