diff --git a/pyproject.toml b/pyproject.toml index d0c83e2..6cd916d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "v2xflexstack" -version = "0.10.9" +version = "0.10.10" authors = [ { name = "Jordi Marias-i-Parella", email = "jordi.marias@i2cat.net" }, { name = "Daniel Ulied Guevara", email = "daniel.ulied@i2cat.net" }, diff --git a/src/flexstack/facilities/ca_basic_service/cam_asn1.py b/src/flexstack/facilities/ca_basic_service/cam_asn1.py index 00dcfac..79670a3 100644 --- a/src/flexstack/facilities/ca_basic_service/cam_asn1.py +++ b/src/flexstack/facilities/ca_basic_service/cam_asn1.py @@ -1,5 +1,5 @@ # pylint: skip-file -from flexstack.utils.asn1.etsi_its_cdd import ETSI_ITS_CDD_ASN1_DESCRIPTIONS +from ...utils.asn1.etsi_its_cdd import ETSI_ITS_CDD_ASN1_DESCRIPTIONS CAM_ASN1_DESCRIPTIONS = ( ETSI_ITS_CDD_ASN1_DESCRIPTIONS diff --git a/src/flexstack/facilities/ca_basic_service/cam_ldm_adaptation.py b/src/flexstack/facilities/ca_basic_service/cam_ldm_adaptation.py index ded4263..9d42b2f 100644 --- a/src/flexstack/facilities/ca_basic_service/cam_ldm_adaptation.py +++ b/src/flexstack/facilities/ca_basic_service/cam_ldm_adaptation.py @@ -29,7 +29,12 @@ class CABasicServiceLDM: Time that the messages stored in the LDM will be mantained. (In milliseconds). """ - def __init__(self, ldm: LDMFacility, access_permissions: tuple[AccessPermission, ...], time_validity: int): + def __init__( + self, + ldm: LDMFacility, + access_permissions: tuple[AccessPermission, ...], + time_validity: int, + ): self.logging = logging.getLogger("ca_basic_service") self.ldm_if_ldm_3 = ldm.if_ldm_3 self.access_permissions = access_permissions diff --git a/src/flexstack/facilities/ca_basic_service/cam_reception_management.py b/src/flexstack/facilities/ca_basic_service/cam_reception_management.py index bd6e314..c563454 100644 --- a/src/flexstack/facilities/ca_basic_service/cam_reception_management.py +++ b/src/flexstack/facilities/ca_basic_service/cam_reception_management.py @@ -73,7 +73,7 @@ def reception_callback(self, btp_indication: BTPDataIndication) -> None: cam["utc_timestamp"] = utc_timestamp if self.ca_basic_service_ldm is not None: self.ca_basic_service_ldm.add_provider_data_to_ldm(cam) - self.logging.debug( + self.logging.info( "Received CAM with timestamp: %s, station_id: %s", cam["cam"]["generationDeltaTime"], cam["header"]["stationId"], diff --git a/src/flexstack/facilities/ca_basic_service/cam_transmission_management.py b/src/flexstack/facilities/ca_basic_service/cam_transmission_management.py index 51ccf01..8ae94e4 100644 --- a/src/flexstack/facilities/ca_basic_service/cam_transmission_management.py +++ b/src/flexstack/facilities/ca_basic_service/cam_transmission_management.py @@ -3,6 +3,7 @@ This file implements the CAM Transmission Management required by the CAM Basic Service. """ + from __future__ import annotations from math import trunc import logging @@ -17,7 +18,7 @@ CommunicationProfile, TrafficClass, ) -from ...utils.time_service import TimeService, ITS_EPOCH_MS, ELAPSED_MILLISECONDS +from ...utils.time_service import ITS_EPOCH_MS, ELAPSED_MILLISECONDS from .cam_ldm_adaptation import CABasicServiceLDM T_GEN_CAM_MIN = 100 # T_GenCamMin [in ms] @@ -50,13 +51,16 @@ class VehicleData: vehicle_width : int Vehicle Width as specified in ETSI TS 102 894-2 V2.3.1 (2024-08). """ + station_id: int = 0 station_type: int = 0 drive_direction: str = "unavailable" - vehicle_length: dict = field(default_factory=lambda: { - "vehicleLengthValue": 1023, - "vehicleLengthConfidenceIndication": "unavailable", - }) + vehicle_length: dict = field( + default_factory=lambda: { + "vehicleLengthValue": 1023, + "vehicleLengthConfidenceIndication": "unavailable", + } + ) vehicle_width: int = 62 def __check_valid_station_id(self) -> None: @@ -69,11 +73,13 @@ def __check_valid_station_type(self) -> None: def __check_valid_drive_direction(self) -> None: if self.drive_direction not in ["forward", "backward", "unavailable"]: - raise ValueError( - "Drive Direction must be forward, backward or unavailable") + raise ValueError("Drive Direction must be forward, backward or unavailable") def __check_valid_vehicle_length(self) -> None: - if self.vehicle_length["vehicleLengthValue"] < 0 or self.vehicle_length["vehicleLengthValue"] > 1023: + if ( + self.vehicle_length["vehicleLengthValue"] < 0 + or self.vehicle_length["vehicleLengthValue"] > 1023 + ): raise ValueError("Vehicle length must be between 0 and 1023") def __check_valid_vehicle_width(self) -> None: @@ -105,6 +111,7 @@ class GenerationDeltaTime: msec : int Time in milliseconds. """ + msec: int = 0 @classmethod @@ -138,12 +145,19 @@ def as_timestamp_in_certain_point(self, utc_timestamp_in_millis: int) -> float: Timestamp of the generation delta time in milliseconds """ number_of_cycles = trunc( - (utc_timestamp_in_millis - ITS_EPOCH_MS + ELAPSED_MILLISECONDS) / 65536) - transformed_timestamp = self.msec + 65536 * \ - number_of_cycles + ITS_EPOCH_MS - ELAPSED_MILLISECONDS + (utc_timestamp_in_millis - ITS_EPOCH_MS + ELAPSED_MILLISECONDS) / 65536 + ) + transformed_timestamp = ( + self.msec + 65536 * number_of_cycles + ITS_EPOCH_MS - ELAPSED_MILLISECONDS + ) if transformed_timestamp <= utc_timestamp_in_millis: return transformed_timestamp - return self.msec + 65536 * (number_of_cycles - 1) + ITS_EPOCH_MS - ELAPSED_MILLISECONDS + return ( + self.msec + + 65536 * (number_of_cycles - 1) + + ITS_EPOCH_MS + - ELAPSED_MILLISECONDS + ) def __gt__(self, other: object) -> bool: """ @@ -208,8 +222,10 @@ class CooperativeAwarenessMessage: All the CAM message in dict format as decoded by the CAMCoder. """ + cam: dict = field( - default_factory=lambda: CooperativeAwarenessMessage.generate_white_cam_static()) + default_factory=lambda: CooperativeAwarenessMessage.generate_white_cam_static() + ) @staticmethod def generate_white_cam_static() -> dict: @@ -307,7 +323,8 @@ def fullfill_gen_delta_time_with_tpv_data(self, tpv: dict) -> None: """ if "time" in tpv: gen_delta_time = GenerationDeltaTime.from_timestamp( - parser.parse(tpv["time"]).timestamp()) + parser.parse(tpv["time"]).timestamp() + ) self.cam["cam"]["generationDeltaTime"] = int(gen_delta_time.msec) def fullfill_basic_container_with_tpv_data(self, tpv: dict) -> None: @@ -362,7 +379,7 @@ def fullfill_high_frequency_container_with_tpv_data(self, tpv: dict) -> None: if "track" in tpv.keys(): self.cam["cam"]["camParameters"]["highFrequencyContainer"][1]["heading"][ "headingValue" - ] = int(tpv["track"]*10) + ] = int(tpv["track"] * 10) if "epd" in tpv.keys(): self.cam["cam"]["camParameters"]["highFrequencyContainer"][1]["heading"][ "headingConfidence" @@ -622,11 +639,7 @@ def _send_cam(self, cam: CooperativeAwarenessMessage) -> None: Send the next CAM. """ if self.ca_basic_service_ldm is not None: - cam_ldm = cam.cam.copy() - cam_ldm["utc_timestamp"] = TimeService.time() - self.ca_basic_service_ldm.add_provider_data_to_ldm( - cam.cam - ) + self.ca_basic_service_ldm.add_provider_data_to_ldm(cam.cam) data = self.cam_coder.encode(cam.cam) request = BTPDataRequest( btp_type=CommonNH.BTP_B, @@ -639,7 +652,7 @@ def _send_cam(self, cam: CooperativeAwarenessMessage) -> None: ) self.btp_router.btp_data_request(request) - self.logging.debug( + self.logging.info( "Sent CAM message with timestamp: %d, station_id: %d", cam.cam["cam"]["generationDeltaTime"], cam.cam["header"]["stationId"], diff --git a/src/flexstack/facilities/decentralized_environmental_notification_service/denm_reception_management.py b/src/flexstack/facilities/decentralized_environmental_notification_service/denm_reception_management.py index 411dd65..6a24fab 100644 --- a/src/flexstack/facilities/decentralized_environmental_notification_service/denm_reception_management.py +++ b/src/flexstack/facilities/decentralized_environmental_notification_service/denm_reception_management.py @@ -100,7 +100,7 @@ def reception_callback(self, btp_indication: BTPDataIndication) -> None: """ denm = self.denm_coder.decode(btp_indication.data) self.feed_ldm(denm) - self.logging.debug( + self.logging.info( "Received DENM with timestamp: %s, station_id: %s", denm["denm"]["management"]["referenceTime"], denm["header"]["stationId"], diff --git a/src/flexstack/facilities/decentralized_environmental_notification_service/denm_transmission_management.py b/src/flexstack/facilities/decentralized_environmental_notification_service/denm_transmission_management.py index 6c09619..e7b440b 100644 --- a/src/flexstack/facilities/decentralized_environmental_notification_service/denm_transmission_management.py +++ b/src/flexstack/facilities/decentralized_environmental_notification_service/denm_transmission_management.py @@ -267,7 +267,7 @@ def transmit_denm( length=len(data), ) self.btp_router.btp_data_request(request) - self.logging.debug( + self.logging.info( "Sent DENM with timestamp: %s, station_id: %s", denm_to_send.denm["denm"]["management"]["referenceTime"], denm_to_send.denm["header"]["stationId"], diff --git a/src/flexstack/facilities/local_dynamic_map/dictionary_database.py b/src/flexstack/facilities/local_dynamic_map/dictionary_database.py index 82b5426..032c490 100644 --- a/src/flexstack/facilities/local_dynamic_map/dictionary_database.py +++ b/src/flexstack/facilities/local_dynamic_map/dictionary_database.py @@ -50,19 +50,23 @@ def _create_query_search(self, query_with_attribute, operator, ref_value): return OPERATOR_MAPPING[operator](query_with_attribute, ref_value) raise ValueError(f"Invalid operator: {operator}") - def _filter_data(self, data_filter: Filter, database: list[dict]) -> tuple[dict, ...]: + def _filter_data( + self, data_filter: Filter, database: list[dict] + ) -> tuple[dict, ...]: list_of_data = [] if data_filter.filter_statement_2 is not None: if str(data_filter.logical_operator) == "and": for data in database: if self._create_query_search( self._get_nested( - data, str(data_filter.filter_statement_1.attribute)), + data, str(data_filter.filter_statement_1.attribute) + ), str(data_filter.filter_statement_1.operator), data_filter.filter_statement_1.ref_value, ) & self._create_query_search( self._get_nested( - data, str(data_filter.filter_statement_2.attribute)), + data, str(data_filter.filter_statement_2.attribute) + ), str(data_filter.filter_statement_2.operator), data_filter.filter_statement_2.ref_value, ): @@ -71,12 +75,14 @@ def _filter_data(self, data_filter: Filter, database: list[dict]) -> tuple[dict, for data in database: if self._create_query_search( self._get_nested( - data, str(data_filter.filter_statement_1.attribute)), + data, str(data_filter.filter_statement_1.attribute) + ), str(data_filter.filter_statement_1.operator), data_filter.filter_statement_1.ref_value, ) | self._create_query_search( self._get_nested( - data, str(data_filter.filter_statement_2.attribute)), + data, str(data_filter.filter_statement_2.attribute) + ), str(data_filter.filter_statement_2.operator), data_filter.filter_statement_2.ref_value, ): @@ -85,7 +91,8 @@ def _filter_data(self, data_filter: Filter, database: list[dict]) -> tuple[dict, for data in database: if self._create_query_search( self._get_nested( - data, str(data_filter.filter_statement_1.attribute)), + data, str(data_filter.filter_statement_1.attribute) + ), str(data_filter.filter_statement_1.operator), data_filter.filter_statement_1.ref_value, ): @@ -108,12 +115,17 @@ def search(self, data_request: RequestDataObjectsReq) -> tuple[dict, ...]: """ with self._lock: if data_request.filter is None: - return RequestDataObjectsReq.filter_out_by_data_object_type(self.all(), data_request.data_object_type) + return tuple( + RequestDataObjectsReq.filter_out_by_data_object_type( + tuple(self.all()), data_request.data_object_type + ) + ) try: return self._filter_data( data_request.filter, RequestDataObjectsReq.filter_out_by_data_object_type( - tuple(self.all()), data_request.data_object_type), + tuple(self.all()), data_request.data_object_type + ), ) except KeyError as e: print(f"[ListDatabase] KeyError searching data: {str(e)}") diff --git a/src/flexstack/facilities/local_dynamic_map/ldm_classes.py b/src/flexstack/facilities/local_dynamic_map/ldm_classes.py index 40986bc..6e48183 100644 --- a/src/flexstack/facilities/local_dynamic_map/ldm_classes.py +++ b/src/flexstack/facilities/local_dynamic_map/ldm_classes.py @@ -24,10 +24,7 @@ from enum import IntEnum import math -from .ldm_constants import ( - EATH_RADIUS, - DATA_OBJECT_TYPE_ID -) +from .ldm_constants import EATH_RADIUS, DATA_OBJECT_TYPE_ID @total_ordering @@ -41,10 +38,13 @@ class TimestampIts: timestamp : int The timestamp in ETSI Timestamp format (milliseconds since 1st January 2004). """ + timestamp_its: int @staticmethod - def initialize_with_utc_timestamp_seconds(utc_timestamp_seconds: int | None = None) -> TimestampIts: + def initialize_with_utc_timestamp_seconds( + utc_timestamp_seconds: int | None = None, + ) -> TimestampIts: """ Initializes the TimestampIts class with a given UTC timestamp in seconds. @@ -59,14 +59,22 @@ def initialize_with_utc_timestamp_seconds(utc_timestamp_seconds: int | None = No An instance of the TimestampIts class. """ if utc_timestamp_seconds: - return TimestampIts(TimestampIts.transform_utc_seconds_timestamp_to_timestamp_its( - utc_timestamp_seconds)) + return TimestampIts( + TimestampIts.transform_utc_seconds_timestamp_to_timestamp_its( + utc_timestamp_seconds + ) + ) else: - return TimestampIts(TimestampIts.transform_utc_seconds_timestamp_to_timestamp_its( - int(TimeService.time()))) + return TimestampIts( + TimestampIts.transform_utc_seconds_timestamp_to_timestamp_its( + int(TimeService.time()) + ) + ) @staticmethod - def transform_utc_seconds_timestamp_to_timestamp_its(utc_timestamp_seconds: int) -> int: + def transform_utc_seconds_timestamp_to_timestamp_its( + utc_timestamp_seconds: int, + ) -> int: """ Method to transform a UTC timestamp to a ETSI ITS timestamp. @@ -165,6 +173,7 @@ class TimeValidity: time : int The time validity in Normal Unix Format (Seconds). """ + time: int def to_etsi_its(self) -> int: @@ -183,6 +192,7 @@ class AccessPermission(IntEnum): """ Class that represents Access Permissions. """ + DENM = 1 CAM = 2 POI = 3 @@ -247,6 +257,7 @@ class AuthorizeReg: """ Class that represents an authorization request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int access_permissions: tuple[AccessPermission, ...] @@ -256,6 +267,7 @@ class AuthorizeResp: """ Class that represents an authorization response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int access_permissions: tuple[AccessPermission, ...] result: AuthorizationResult @@ -267,10 +279,8 @@ class RevocationReason(IntEnum): def __str__(self) -> str: return { - RevocationReason.REGISTRATION_REVOKED_BY_REGISTRATION_AUTHORITY: - "registrationRevokedByRegistrationAuthority", - RevocationReason.REGISTRATION_PERIOD_EXPIRED: - "registrationPeriodExpired", + RevocationReason.REGISTRATION_REVOKED_BY_REGISTRATION_AUTHORITY: "registrationRevokedByRegistrationAuthority", + RevocationReason.REGISTRATION_PERIOD_EXPIRED: "registrationPeriodExpired", }[self] @@ -292,6 +302,7 @@ class RevokeAuthorizationReg: """ Class that represents a Revoke Authorization Registration as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int reason: RevocationReason @@ -301,6 +312,7 @@ class RegisterDataProviderReq: """ Class that represents a Register Data Provider Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int access_permissions: tuple[AccessPermission, ...] time_validity: TimeValidity @@ -358,6 +370,7 @@ class RegisterDataProviderResult(IntEnum): """ Class that represents a Register Data Provider Result as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + ACCEPTED = 0 REJECTED = 1 @@ -373,6 +386,7 @@ class RegisterDataProviderResp: """ Class that represent a Register Data Provder Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int access_permisions: tuple[AccessPermission, ...] result: RegisterDataProviderResult @@ -382,6 +396,7 @@ class DeregisterDataProviderAck(IntEnum): """ Class that represent Deregister Data Provider Ack as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + ACCEPTED = 0 REJECTED = 1 @@ -397,6 +412,7 @@ class DeregisterDataProviderReq: """ Class that represents Deregister Data Provider Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int @@ -405,6 +421,7 @@ class DeregisterDataProviderResp: """ Class that represents Deregister Data Provider Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int result: DeregisterDataProviderAck @@ -414,6 +431,7 @@ class RevokeDataProviderRegistrationResp: """ Class that represents Revoke Data Provider Registration Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int @@ -422,6 +440,7 @@ class PositionConfidenceEllipse: """ Class that represents Position Confidence Ellipse as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + semi_major_confidence: int semi_minor_confidence: int semi_major_orientation: int @@ -432,6 +451,7 @@ class Altitude: """ Class that represents Altitude as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + altitude_value: int altitude_confidence: int @@ -493,6 +513,7 @@ class ReferencePosition: """ Class that represent Reference Position as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + latitude: int longitude: int position_confidence_ellipse: PositionConfidenceEllipse @@ -558,6 +579,7 @@ class StationType: """ Class that represent Station Type as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + station_type: int def __str__(self) -> str: @@ -585,6 +607,7 @@ class Direction: """ Class that represents Direction as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + direction: int def __str__(self) -> str: @@ -604,6 +627,7 @@ class Circle: """ Class that represents Circle as speficied in ETSI EN 302 895 V1.1.1 (2014-09). """ + radius: int @@ -612,6 +636,7 @@ class Rectangle: """ Class that represents Rectangle as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + a_semi_axis: int b_semi_axis: int azimuth_angle: Direction @@ -622,6 +647,7 @@ class Ellipse: """ Class that represents Ellipse as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + a_semi_axis: int b_semi_axis: int azimuth_angle: Direction @@ -647,6 +673,7 @@ class RelevanceDistance: """ Class that represents Relevance Distance as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + relevance_distance: int def __str__(self) -> str: @@ -698,6 +725,7 @@ class RelevanceArea: """ Class that represents Relevance Area as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + relevance_distance: RelevanceDistance relevance_traffic_direction: RelevanceTrafficDirection @@ -707,6 +735,7 @@ class GeometricArea: """ Class that represents Geometric Area as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + circle: Circle | None rectangle: Rectangle | None ellipse: Ellipse | None @@ -717,6 +746,7 @@ class ReferenceArea: """ Class that represents Reference Area as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + geometric_area: GeometricArea relevance_area: RelevanceArea @@ -756,15 +786,18 @@ def initializer( semi_major_orientation=semi_major_orientation, semi_minor_confidence=semi_minor_confidence, ), - altitude=Altitude(altitude_value=altitude_value, - altitude_confidence=altitude_confidence), + altitude=Altitude( + altitude_value=altitude_value, altitude_confidence=altitude_confidence + ), ) reference_area = ReferenceArea( - geometric_area=GeometricArea(circle=Circle( - radius=radius), rectangle=rectangle, ellipse=ellipse), + geometric_area=GeometricArea( + circle=Circle(radius=radius), rectangle=rectangle, ellipse=ellipse + ), relevance_area=RelevanceArea( relevance_distance=RelevanceDistance( - relevance_distance=relevance_distance), + relevance_distance=relevance_distance + ), relevance_traffic_direction=RelevanceTrafficDirection( relevance_traffic_direction ), @@ -786,7 +819,9 @@ def location_service_callback(self, tpv: dict) -> None: self.reference_position.update_with_gpsd_tpv(tpv) @staticmethod - def location_builder_circle(latitude: int, longitude: int, altitude: int, radius: int) -> "Location": + def location_builder_circle( + latitude: int, longitude: int, altitude: int, radius: int + ) -> "Location": """ Static method to create a location, ETSI class, with a circle as the geometric area as defined in ETSI TS 102 894-2 V2.2.1 (2023-10). @@ -819,14 +854,17 @@ def location_builder_circle(latitude: int, longitude: int, altitude: int, radius ) reference_area = ReferenceArea( - geometric_area=GeometricArea(circle=Circle( - radius=radius), rectangle=None, ellipse=None), + geometric_area=GeometricArea( + circle=Circle(radius=radius), rectangle=None, ellipse=None + ), relevance_area=RelevanceArea( relevance_distance=RelevanceDistance(relevance_distance=1), relevance_traffic_direction=RelevanceTrafficDirection(0), ), ) - return Location(reference_area=reference_area, reference_position=reference_position) + return Location( + reference_area=reference_area, reference_position=reference_position + ) @dataclass(frozen=True) @@ -834,6 +872,7 @@ class AddDataProviderReq: """ Class that represents Add Data Provider Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int timestamp: TimestampIts location: Location @@ -861,8 +900,11 @@ def __iter__(self): "referenceArea": { "geometricArea": { "circle": ( - {"radius": self.location.reference_area.geometric_area.circle.radius} - if self.location.reference_area.geometric_area.circle is not None + { + "radius": self.location.reference_area.geometric_area.circle.radius + } + if self.location.reference_area.geometric_area.circle + is not None else None ), "rectangle": ( @@ -871,7 +913,8 @@ def __iter__(self): "bSemiAxis": self.location.reference_area.geometric_area.rectangle.b_semi_axis, "azimuthAngle": self.location.reference_area.geometric_area.rectangle.azimuth_angle, } - if self.location.reference_area.geometric_area.rectangle is not None + if self.location.reference_area.geometric_area.rectangle + is not None else None ), "ellipse": ( @@ -880,7 +923,8 @@ def __iter__(self): "bSemiAxis": self.location.reference_area.geometric_area.ellipse.b_semi_axis, "azimuthAngle": self.location.reference_area.geometric_area.ellipse.azimuth_angle, } - if self.location.reference_area.geometric_area.ellipse is not None + if self.location.reference_area.geometric_area.ellipse + is not None else None ), }, @@ -928,8 +972,11 @@ def to_dict(self) -> dict: "referenceArea": { "geometricArea": { "circle": ( - {"radius": self.location.reference_area.geometric_area.circle.radius} - if self.location.reference_area.geometric_area.circle is not None + { + "radius": self.location.reference_area.geometric_area.circle.radius + } + if self.location.reference_area.geometric_area.circle + is not None else None ), "rectangle": ( @@ -938,7 +985,8 @@ def to_dict(self) -> dict: "bSemiAxis": self.location.reference_area.geometric_area.rectangle.b_semi_axis, "azimuthAngle": self.location.reference_area.geometric_area.rectangle.azimuth_angle, } - if self.location.reference_area.geometric_area.rectangle is not None + if self.location.reference_area.geometric_area.rectangle + is not None else None ), "ellipse": ( @@ -947,7 +995,8 @@ def to_dict(self) -> dict: "bSemiAxis": self.location.reference_area.geometric_area.ellipse.b_semi_axis, "azimuthAngle": self.location.reference_area.geometric_area.ellipse.azimuth_angle, } - if self.location.reference_area.geometric_area.ellipse is not None + if self.location.reference_area.geometric_area.ellipse + is not None else None ), }, @@ -992,15 +1041,20 @@ def from_dict(data: dict) -> "AddDataProviderReq": longitude=reference_position_data.get("longitude"), position_confidence_ellipse=PositionConfidenceEllipse( semi_major_confidence=reference_position_data[ - "positionConfidenceEllipse"]["semiMajorConfidence"], + "positionConfidenceEllipse" + ]["semiMajorConfidence"], semi_minor_confidence=reference_position_data[ - "positionConfidenceEllipse"]["semiMinorConfidence"], + "positionConfidenceEllipse" + ]["semiMinorConfidence"], semi_major_orientation=reference_position_data[ - "positionConfidenceEllipse"]["semiMajorOrientation"], + "positionConfidenceEllipse" + ]["semiMajorOrientation"], ), altitude=Altitude( altitude_value=reference_position_data["altitude"]["altitudeValue"], - altitude_confidence=reference_position_data["altitude"]["altitudeConfidence"], + altitude_confidence=reference_position_data["altitude"][ + "altitudeConfidence" + ], ), ) reference_area_data = location_data.get("referenceArea") @@ -1008,40 +1062,60 @@ def from_dict(data: dict) -> "AddDataProviderReq": geometric_area=GeometricArea( circle=( Circle( - radius=reference_area_data["geometricArea"]["circle"]["radius"]) + radius=reference_area_data["geometricArea"]["circle"]["radius"] + ) if reference_area_data["geometricArea"]["circle"] else Circle(radius=0) ), rectangle=( Rectangle( - a_semi_axis=reference_area_data["geometricArea"]["rectangle"]["aSemiAxis"], - b_semi_axis=reference_area_data["geometricArea"]["rectangle"]["bSemiAxis"], - azimuth_angle=reference_area_data["geometricArea"]["rectangle"]["azimuthAngle"], + a_semi_axis=reference_area_data["geometricArea"]["rectangle"][ + "aSemiAxis" + ], + b_semi_axis=reference_area_data["geometricArea"]["rectangle"][ + "bSemiAxis" + ], + azimuth_angle=reference_area_data["geometricArea"]["rectangle"][ + "azimuthAngle" + ], ) if reference_area_data["geometricArea"]["rectangle"] - else Rectangle(a_semi_axis=0, b_semi_axis=0, azimuth_angle=Direction(0)) + else Rectangle( + a_semi_axis=0, b_semi_axis=0, azimuth_angle=Direction(0) + ) ), ellipse=( Ellipse( - a_semi_axis=reference_area_data["geometricArea"]["ellipse"]["aSemiAxis"], - b_semi_axis=reference_area_data["geometricArea"]["ellipse"]["bSemiAxis"], - azimuth_angle=reference_area_data["geometricArea"]["ellipse"]["azimuthAngle"], + a_semi_axis=reference_area_data["geometricArea"]["ellipse"][ + "aSemiAxis" + ], + b_semi_axis=reference_area_data["geometricArea"]["ellipse"][ + "bSemiAxis" + ], + azimuth_angle=reference_area_data["geometricArea"]["ellipse"][ + "azimuthAngle" + ], ) if reference_area_data["geometricArea"]["ellipse"] - else Ellipse(a_semi_axis=0, b_semi_axis=0, azimuth_angle=Direction(0)) + else Ellipse( + a_semi_axis=0, b_semi_axis=0, azimuth_angle=Direction(0) + ) ), ), relevance_area=RelevanceArea( relevance_distance=RelevanceDistance( - relevance_distance=reference_area_data["relevanceArea"]["relevanceDistance"] + relevance_distance=reference_area_data["relevanceArea"][ + "relevanceDistance" + ] ), - relevance_traffic_direction=RelevanceTrafficDirection(reference_area_data[ - "relevanceArea"]["relevanceTrafficDirection"] + relevance_traffic_direction=RelevanceTrafficDirection( + reference_area_data["relevanceArea"]["relevanceTrafficDirection"] ), ), ) location = Location( - reference_position=reference_position, reference_area=reference_area) + reference_position=reference_position, reference_area=reference_area + ) return AddDataProviderReq( application_id=application_id, @@ -1057,6 +1131,7 @@ class AddDataProviderResp: """ Class that represents Add Data Provider Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int data_object_id: int @@ -1096,7 +1171,9 @@ def from_dict(data: dict) -> "AddDataProviderResp": application_id = data.get("application_id", 0) data_object_id = data.get("data_object_id", 0) - return AddDataProviderResp(application_id=application_id, data_object_id=data_object_id) + return AddDataProviderResp( + application_id=application_id, data_object_id=data_object_id + ) @dataclass(frozen=True) @@ -1104,6 +1181,7 @@ class UpdateDataProviderReq: """ Class that represents Update Data Provider Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int data_object_id: int time_stamp: TimestampIts @@ -1116,6 +1194,7 @@ class UpdateDataProviderResult(IntEnum): """ Class that represents Update Data Provider Result as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + SUCCEED = 0 UNKNOWN_DATA_OBJECT_ID = 1 INCONSISTENT_DATA_OBJECT_TYPE = 2 @@ -1124,7 +1203,7 @@ def __str__(self) -> str: return { UpdateDataProviderResult.SUCCEED: "succeed", UpdateDataProviderResult.UNKNOWN_DATA_OBJECT_ID: "unknownDataObjectID", - UpdateDataProviderResult.INCONSISTENT_DATA_OBJECT_TYPE: "inconsistentDataObjectType" + UpdateDataProviderResult.INCONSISTENT_DATA_OBJECT_TYPE: "inconsistentDataObjectType", }[self] @@ -1133,6 +1212,7 @@ class UpdateDataProviderResp: """ Class that represents Update Data Provider Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int data_object_id: int result: UpdateDataProviderResult @@ -1143,6 +1223,7 @@ class DeleteDataProviderReq: """ Class that represents Delete Data Provider Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int data_object_id: int time_stamp: TimestampIts @@ -1152,6 +1233,7 @@ class DeleteDataProviderResult(IntEnum): """ Class that represents Delete Data Provider Result as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + SUCCEED = 0 FAILED = 1 @@ -1167,6 +1249,7 @@ class DeleteDataProviderResp: """ Class that represents Delete Data Provider Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int data_object_id: int result: DeleteDataProviderResult @@ -1177,6 +1260,7 @@ class RegisterDataConsumerReq: """ Class that represents Register Data Consumer Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int access_permisions: tuple[AccessPermission, ...] area_of_interest: GeometricArea @@ -1186,6 +1270,7 @@ class RegisterDataConsumerResult(IntEnum): """ Class that represents Register Data Consumer Result as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + ACCEPTED = 0 WARNING = 1 REJECTED = 2 @@ -1203,6 +1288,7 @@ class RegisterDataConsumerResp: """ Class that represents Register Data Consumer Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int access_permisions: tuple[AccessPermission, ...] result: RegisterDataConsumerResult @@ -1213,6 +1299,7 @@ class DeregisterDataConsumerReq: """ Class that represents Deregister Data Consumer Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int @@ -1220,6 +1307,7 @@ class DeregisterDataConsumerAck(IntEnum): """ Class that represents Deegister Data Consumer Ack as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + SUCCEED = 0 FAILED = 1 @@ -1235,6 +1323,7 @@ class DeregisterDataConsumerResp: """ Class that represents Deregister Data Consumer Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int ack: DeregisterDataConsumerAck @@ -1244,6 +1333,7 @@ class UnsubscribeDataConsumerReq: """ Class that represents Unsubscribe Data Consumer Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int subscription_id: int @@ -1252,6 +1342,7 @@ class UnsubscribeDataConsumerAck(IntEnum): """ Class that represents Unsubscribe Data Consumer Ack as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + ACCEPTED = 0 FAILED = 1 @@ -1267,6 +1358,7 @@ class UnsubscribeDataConsumerResp: """ Class that represents Unsubscribe Data Consumer Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int subscription_id: int result: UnsubscribeDataConsumerAck @@ -1277,6 +1369,7 @@ class RevokeDataConsumerRegistrationResp: """ Class that represents Revoke Data Consumer Registration Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int @@ -1284,6 +1377,7 @@ class OrderingDirection(IntEnum): """ Class that represents Ordering Direction as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + ASCENDING = 0 DESCENDING = 1 @@ -1309,6 +1403,7 @@ class OrderTupleValue: ordering_direction: OrderingDirection OrderingDirection class that represents what direction to be ordered. """ + attribute: str ordering_direction: OrderingDirection @@ -1317,6 +1412,7 @@ class LogicalOperators(IntEnum): """ Class that represents Logical Operators as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + AND = 0 OR = 1 @@ -1331,6 +1427,7 @@ class ComparisonOperators(IntEnum): """ Class that represents Comparison Operators as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + EQUAL = 0 # == NOT_EQUAL = 1 # != GREATER_THAN = 2 # > @@ -1358,6 +1455,7 @@ class FilterStatement: """ Class that represents Filter Statement as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + attribute: str operator: ComparisonOperators ref_value: int @@ -1368,6 +1466,7 @@ class Filter: """ Class that represents Filter as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + filter_statement_1: FilterStatement logical_operator: LogicalOperators | None = None filter_statement_2: FilterStatement | None = None @@ -1378,14 +1477,17 @@ class RequestDataObjectsReq: """ Class that represents Request Data Objects Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int data_object_type: tuple[int, ...] - priority: int - order: tuple[OrderTupleValue, ...] - filter: Filter + priority: int | None + order: tuple[OrderTupleValue, ...] | None + filter: Filter | None @staticmethod - def filter_out_by_data_object_type(search_result: tuple[dict, ...], data_object_types: tuple[int, ...]) -> list[dict]: + def filter_out_by_data_object_type( + search_result: tuple[dict, ...], data_object_types: tuple[int, ...] + ) -> list[dict]: """ Function that filters out all packets that are not part of the specified data object type list given in the RequestDataObjectReq @@ -1404,7 +1506,12 @@ def filter_out_by_data_object_type(search_result: tuple[dict, ...], data_object_ """ filtered_search_result = [] for result in search_result: - if RequestDataObjectsReq.get_object_type_from_data_object(result["dataObject"]) in data_object_types: + if ( + RequestDataObjectsReq.get_object_type_from_data_object( + result["dataObject"] + ) + in data_object_types + ): filtered_search_result.append(result) return filtered_search_result @@ -1432,6 +1539,7 @@ class RequestedDataObjectsResult(IntEnum): """ Class that represents Requested Data Objects Result as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + SUCCEED = 0 INVALID_ITSA_ID = 1 INVALID_DATA_OBJECT_TYPE = 2 @@ -1456,6 +1564,7 @@ class RequestDataObjectsResp: """ Class that represents Request Data Objects Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int data_objects: tuple[dict, ...] result: RequestedDataObjectsResult @@ -1520,8 +1629,7 @@ def find_attribute_static(attribute: str, data_object: dict) -> list: if key == attribute: return [key] if isinstance(value, dict): - path = RequestDataObjectsResp.find_attribute_static( - attribute, value) + path = RequestDataObjectsResp.find_attribute_static(attribute, value) if path: return [key] + path return [] @@ -1532,6 +1640,7 @@ class SubscribeDataobjectsReq: """ As specified in facilities.local_dynamic_map.if_ldm_4.py this class has been modified to fit implementation. """ + application_id: int data_object_type: tuple[int, ...] priority: int | None = None @@ -1546,6 +1655,7 @@ class SubscriptionInfo: """ Non-standard class that represents Subscription Info for internal use. """ + subscription_request: SubscribeDataobjectsReq callback: Callable[[RequestDataObjectsResp], None] @@ -1554,6 +1664,7 @@ class SubscribeDataobjectsResult(IntEnum): """ Class that represents Subscribe Data Objects Result as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + SUCCESSFUL = 0 INVALID_ITSA_ID = 1 INVALID_DATA_OBJECT_TYPE = 2 @@ -1581,6 +1692,7 @@ class SubscribeDataObjectsResp: """ Class that represents Subscribe Data Objects Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int subscription_id: int result: SubscribeDataobjectsResult @@ -1592,6 +1704,7 @@ class PublishDataobjects: """ Class that represents Publish Data Objects as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + subscription_id: int requested_data: tuple[str, ...] @@ -1601,6 +1714,7 @@ class UnsubscribeDataobjectsReq: """ Class that represents Unsubscribe Data Objects Request as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int subscription_id: int @@ -1609,6 +1723,7 @@ class UnsubscribeDataobjectsResult(IntEnum): """ Class that represents Unsubscribe Data Objects Result as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + ACCEPTED = 0 REJECTED = 1 @@ -1624,6 +1739,7 @@ class UnsubscribeDataobjectsResp: """ Class that represents Unsubscribe Data Objects Response as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + application_id: int subscription_id: int result: UnsubscribeDataobjectsResult @@ -1633,6 +1749,7 @@ class ReferenceValue(IntEnum): """ Class that represents Reference Value as specified in ETSI EN 302 895 V1.1.1 (2014-09). """ + BOOL_VALUE = 0 SBYTE_VALUE = 1 BYTE_VALUE = 2 @@ -1688,7 +1805,10 @@ def haversine_a(dlat: float, lat1: float, lat2: float, dlon: float) -> float: float Haversine value. """ - return math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2 + return ( + math.sin(dlat / 2) ** 2 + + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2 + ) @staticmethod def haversine_c(a: float) -> float: @@ -1714,7 +1834,9 @@ def haversine_c(a: float) -> float: return 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a)) @staticmethod - def haversine_distance(coord1: tuple[float, float], coord2: tuple[float, float]) -> float: + def haversine_distance( + coord1: tuple[float, float], coord2: tuple[float, float] + ) -> float: """ Function that returns the distance between two coordinates in meters. @@ -1817,10 +1939,12 @@ def get_station_id(data_object: dict) -> int | None: The station id. """ station_id = Utils.get_nested( - data_object, Utils.find_attribute("stationID", data_object)) + data_object, Utils.find_attribute("stationID", data_object) + ) if station_id is None: station_id = Utils.get_nested( - data_object, Utils.find_attribute("stationId", data_object)) + data_object, Utils.find_attribute("stationId", data_object) + ) if station_id is None: return None station_id = int(station_id[0]) if station_id[0] is not None else None @@ -1873,7 +1997,9 @@ def convert_etsi_coordinates_to_normal(point: tuple) -> tuple: return tuple(coord / 10**7 for coord in point) @staticmethod - def euclidian_distance(point1: tuple[float, float], point2: tuple[float, float]) -> float: + def euclidian_distance( + point1: tuple[float, float], point2: tuple[float, float] + ) -> float: """ Generated the euclidian distance between two points. diff --git a/src/flexstack/facilities/local_dynamic_map/ldm_service.py b/src/flexstack/facilities/local_dynamic_map/ldm_service.py index 8989da5..03c2fd5 100644 --- a/src/flexstack/facilities/local_dynamic_map/ldm_service.py +++ b/src/flexstack/facilities/local_dynamic_map/ldm_service.py @@ -13,7 +13,7 @@ SubscribeDataobjectsReq, TimestampIts, Utils, - SubscriptionInfo + SubscriptionInfo, ) from .ldm_constants import ( DATA_OBJECT_TYPE_ID, @@ -53,8 +53,7 @@ def __init__(self, ldm_maintenance: LDMMaintenance) -> None: self.data_provider_its_aid: set[int] = set() self.data_consumer_its_aid: set[int] = set() self.subscriptions: list[SubscriptionInfo] = [] - self.last_checked_subscriptions_time: dict[SubscriptionInfo, TimestampIts] = { - } + self.last_checked_subscriptions_time: dict[SubscriptionInfo, TimestampIts] = {} self._lock = threading.RLock() def attend_subscriptions(self) -> None: @@ -68,7 +67,10 @@ def attend_subscriptions(self) -> None: search_result = self.search_data(subscription) if not search_result: continue - if subscription.subscription_request.multiplicity is not None and subscription.subscription_request.multiplicity > len(search_result): + if ( + subscription.subscription_request.multiplicity is not None + and subscription.subscription_request.multiplicity > len(search_result) + ): continue ordered_search_result = search_result @@ -80,7 +82,10 @@ def attend_subscriptions(self) -> None: ordered_search_result = ordered_sequences[0] self.process_notifications(subscription, ordered_search_result) data_consumer_its_aid = self.get_data_consumer_its_aid() - if subscription.subscription_request.application_id not in data_consumer_its_aid: + if ( + subscription.subscription_request.application_id + not in data_consumer_its_aid + ): subscriptions_to_remove.add(subscription) for subscription in subscriptions_to_remove: self.remove_subscription(subscription) @@ -98,16 +103,15 @@ def search_data(self, subscription: SubscriptionInfo) -> tuple[dict, ...]: tuple[dict, ...] tuple of data objects that match the filter. """ - if subscription.subscription_request.filter is not None and subscription.subscription_request.order is not None and subscription.subscription_request.priority is not None: - data_request = RequestDataObjectsReq( - subscription.subscription_request.application_id, - subscription.subscription_request.data_object_type, - subscription.subscription_request.priority, - subscription.subscription_request.order, - subscription.subscription_request.filter, - ) - return self.ldm_maintenance.data_containers.search(data_request) - return () + + data_request = RequestDataObjectsReq( + subscription.subscription_request.application_id, + subscription.subscription_request.data_object_type, + subscription.subscription_request.priority, + subscription.subscription_request.order, + subscription.subscription_request.filter, + ) + return self.ldm_maintenance.data_containers.search(data_request) def filter_data_object_type( self, search_result: tuple[dict, ...], allowed_types: tuple[str, ...] @@ -157,8 +161,7 @@ def process_notifications( current_time = TimestampIts.initialize_with_utc_timestamp_seconds() notify_time = subscription.subscription_request.notify_time with self._lock: - last_checked = self.last_checked_subscriptions_time.get( - subscription) + last_checked = self.last_checked_subscriptions_time.get(subscription) if last_checked is None: self.last_checked_subscriptions_time[subscription] = current_time last_checked = current_time @@ -201,7 +204,9 @@ def find_key_paths_in_list(self, target_key: str, search_result: list) -> list[s key_paths.append(self.find_key_path(target_key, result)) return key_paths - def find_key_path(self, target_key: str, dictionary: dict, path: list | None = None) -> str: + def find_key_path( + self, target_key: str, dictionary: dict, path: list | None = None + ) -> str: """ Static method to find the path of a key in a dictionary. @@ -241,15 +246,16 @@ def order_search_results( tuple[tuple[dict, ...], ...] tuple of ordered tuples of data objects. """ + def build_key(item): return tuple( - Utils.get_nested( - item, Utils.find_attribute(order.attribute, item)) + Utils.get_nested(item, Utils.find_attribute(order.attribute, item)) for order in orders ) - reverse = any(order.ordering_direction == OrderingDirection.DESCENDING - for order in orders) + reverse = any( + order.ordering_direction == OrderingDirection.DESCENDING for order in orders + ) return (tuple(sorted(search_results, key=build_key, reverse=reverse)),) def add_provider_data(self, data: AddDataProviderReq) -> int | None: @@ -318,7 +324,9 @@ def del_data_provider_its_aid(self, its_aid: int) -> None: with self._lock: self.data_provider_its_aid.discard(its_aid) - def query(self, data_request: RequestDataObjectsReq) -> tuple[tuple[dict, ...], ...]: + def query( + self, data_request: RequestDataObjectsReq + ) -> tuple[tuple[dict, ...], ...]: """ Method to query the data containers using the RequestDataObjectsReq object. @@ -340,12 +348,11 @@ def query(self, data_request: RequestDataObjectsReq) -> tuple[tuple[dict, ...], ) except (KeyError, json.decoder.JSONDecodeError) as e: print(f"Error querying data container: {str(e)}") - return (()) + return () # If it does then see if it needs ordering and order it if data_request.order is not None: - search_result = self.order_search_results( - search_result, data_request.order) + search_result = self.order_search_results(search_result, data_request.order) else: search_result = (search_result,) return search_result @@ -390,10 +397,9 @@ def store_new_subscription_petition( callback=callback, ) with self._lock: - self.subscriptions.append( - new_subscription - ) - self.last_checked_subscriptions_time[new_subscription] = TimestampIts.initialize_with_utc_timestamp_seconds( + self.subscriptions.append(new_subscription) + self.last_checked_subscriptions_time[new_subscription] = ( + TimestampIts.initialize_with_utc_timestamp_seconds() ) return hash(new_subscription.subscription_request) diff --git a/src/flexstack/facilities/vru_awareness_service/vam_reception_management.py b/src/flexstack/facilities/vru_awareness_service/vam_reception_management.py index d662d42..a51755a 100644 --- a/src/flexstack/facilities/vru_awareness_service/vam_reception_management.py +++ b/src/flexstack/facilities/vru_awareness_service/vam_reception_management.py @@ -63,7 +63,7 @@ def reception_callback(self, btp_indication: BTPDataIndication) -> None: if self.vru_basic_service_ldm is not None: self.vru_basic_service_ldm.add_provider_data_to_ldm(vam) self.logging.debug("Recieved message; %s", vam) - self.logging.debug( + self.logging.info( "Recieved VAM message with timestamp: %s, station_id: %s", vam["vam"]["generationDeltaTime"], vam["header"]["stationId"], diff --git a/src/flexstack/facilities/vru_awareness_service/vam_transmission_management.py b/src/flexstack/facilities/vru_awareness_service/vam_transmission_management.py index 32bb035..bcf74b4 100644 --- a/src/flexstack/facilities/vru_awareness_service/vam_transmission_management.py +++ b/src/flexstack/facilities/vru_awareness_service/vam_transmission_management.py @@ -680,7 +680,7 @@ def send_next_vam(self, vam: VAMMessage) -> None: length=len(data), ) self.btp_router.btp_data_request(request) - self.logging.debug( + self.logging.info( "Sent VAM message with timestamp: %s, station_id: %s", vam.vam['vam']['generationDeltaTime'], vam.vam['header']['stationId'] diff --git a/src/flexstack/facilities/vru_awareness_service/vru_awareness_service.py b/src/flexstack/facilities/vru_awareness_service/vru_awareness_service.py index a3ab4b6..fc436f1 100644 --- a/src/flexstack/facilities/vru_awareness_service/vru_awareness_service.py +++ b/src/flexstack/facilities/vru_awareness_service/vru_awareness_service.py @@ -1,7 +1,7 @@ from __future__ import annotations import logging -from flexstack.facilities.local_dynamic_map.ldm_classes import AccessPermission +from ..local_dynamic_map.ldm_classes import AccessPermission from .vam_ldm_adaptation import VRUBasicServiceLDM from .vam_transmission_management import VAMTransmissionManagement, DeviceDataProvider from ...btp.router import Router as BTPRouter diff --git a/src/flexstack/geonet/common_header.py b/src/flexstack/geonet/common_header.py index e0cca5b..d127040 100644 --- a/src/flexstack/geonet/common_header.py +++ b/src/flexstack/geonet/common_header.py @@ -149,3 +149,19 @@ def decode_from_bytes(cls, header: bytes) -> "CommonHeader": if len(header) < 8: raise DecodeError("Common Header must be 8 bytes long") return cls.decode_from_int(int.from_bytes(header[0:8], "big")) + + @classmethod + def initialize_beacon(cls) -> CommonHeader: + """ + Initializes a Common Header for a beacon message. + + Returns + ------- + CommonHeader : + Initialized Common Header for a beacon message. + """ + nh = CommonNH.ANY + ht = HeaderType.BEACON + hst = HeaderSubType.UNSPECIFIED + tc = TrafficClass() + return cls(nh=nh, reserved=0, ht=ht, hst=hst, tc=tc, flags=0, pl=0, mhl=1) diff --git a/src/flexstack/geonet/gn_address.py b/src/flexstack/geonet/gn_address.py index 3ec294d..b80d78b 100644 --- a/src/flexstack/geonet/gn_address.py +++ b/src/flexstack/geonet/gn_address.py @@ -90,7 +90,7 @@ def encode_to_address(self) -> int: bytes Encoded ST """ - return (self.value << 3) << 8*7 + return (self.value << 2) << 8*7 class InvalidMIDLength(Exception): @@ -220,7 +220,7 @@ def decode(cls, data: bytes) -> "GNAddress": if len(data) < 8: raise DecodeError("GNAddress must be 8 bytes long") m = M((data[0] & 0x80) >> 7) - st = ST((data[0] & 0x78) >> 3) + st = ST((data[0] & 0x7C) >> 2) mid = MID(data[2:8]) return cls(m=m, st=st, mid=mid) diff --git a/src/flexstack/geonet/mib.py b/src/flexstack/geonet/mib.py index 432bfc6..5d9aa44 100644 --- a/src/flexstack/geonet/mib.py +++ b/src/flexstack/geonet/mib.py @@ -220,7 +220,7 @@ class MIB: itsGnLocationServiceMaxRetrans: int = 10 itsGnLocationServiceRetransmitTimer: int = 1000 itsGnLocationServicePacketBufferSize: int = 1024 - itsGnBeaconServiceRetransmitTimer: int = 3000 + itsGnBeaconServiceRetransmitTimer: int = 0 itsGnBeaconServiceMaxJitter: Optional[float] = None itsGnDefaultHopLimit: int = 10 itsGnDPLLength: int = 8 diff --git a/src/flexstack/geonet/position_vector.py b/src/flexstack/geonet/position_vector.py index 13e3869..2d8ac8a 100644 --- a/src/flexstack/geonet/position_vector.py +++ b/src/flexstack/geonet/position_vector.py @@ -1,6 +1,11 @@ from dateutil import parser from dataclasses import dataclass -from ..utils.time_service import ITS_EPOCH, ITS_EPOCH_MS, ELAPSED_SECONDS, ELAPSED_MILLISECONDS +from ..utils.time_service import ( + ITS_EPOCH, + ITS_EPOCH_MS, + ELAPSED_SECONDS, + ELAPSED_MILLISECONDS, +) from .exceptions import DecodeError from .gn_address import GNAddress @@ -37,12 +42,13 @@ def set_in_normal_timestamp_seconds(cls, utc_timestamp_seconds: float) -> "TST": utc_timestamp_seconds : float Timestamp in normal UTC timestamp format. """ - msec = ((utc_timestamp_seconds - ITS_EPOCH - + ELAPSED_SECONDS) * 1000) % 2 ** 32 + msec = ((utc_timestamp_seconds - ITS_EPOCH + ELAPSED_SECONDS) * 1000) % 2**32 return cls(msec=int(msec)) @classmethod - def set_in_normal_timestamp_milliseconds(cls, utc_timestamp_milliseconds: int) -> "TST": + def set_in_normal_timestamp_milliseconds( + cls, utc_timestamp_milliseconds: int + ) -> "TST": """ Set the timestamp in normal timestamp format. @@ -51,8 +57,9 @@ def set_in_normal_timestamp_milliseconds(cls, utc_timestamp_milliseconds: int) - utc_timestamp_milliseconds : int Timestamp in normal UTC timestamp format. """ - msec = (utc_timestamp_milliseconds - ITS_EPOCH_MS - + ELAPSED_MILLISECONDS) % 2 ** 32 + msec = ( + utc_timestamp_milliseconds - ITS_EPOCH_MS + ELAPSED_MILLISECONDS + ) % 2**32 return cls(msec=int(msec)) def encode(self) -> int: @@ -76,7 +83,7 @@ def decode(cls, data: int) -> "TST": data : int Encoded timestamp. """ - return cls(msec=int(data % 2 ** 32)) + return cls(msec=int(data % 2**32)) def __eq__(self, __o: object) -> bool: """ @@ -128,8 +135,11 @@ def __gt__(self, __o: object) -> bool: True if greater than, False otherwise. """ if isinstance(__o, TST): - return (self.msec > __o.msec) and ((self.msec - __o.msec) <= (2**32)/2) or ( - (__o.msec > self.msec) and ((__o.msec - self.msec) > (2**32)/2)) + return ( + (self.msec > __o.msec) + and ((self.msec - __o.msec) <= (2**32) / 2) + or ((__o.msec > self.msec) and ((__o.msec - self.msec) > (2**32) / 2)) + ) return False def __ge__(self, __o: object) -> bool: @@ -237,7 +247,7 @@ def __str__(self) -> str: str String representation. """ - return str(self.msec)+" msec" + return str(self.msec) + " msec" @dataclass(frozen=True) @@ -282,9 +292,19 @@ def set_gn_addr(self, gn_addr: GNAddress) -> "LongPositionVector": gn_addr : GNAddress GN address. """ - return LongPositionVector(gn_addr=gn_addr, tst=self.tst, latitude=self.latitude, longitude=self.longitude, pai=self.pai, s=self.s, h=self.h) + return LongPositionVector( + gn_addr=gn_addr, + tst=self.tst, + latitude=self.latitude, + longitude=self.longitude, + pai=self.pai, + s=self.s, + h=self.h, + ) - def set_tst_in_normal_timestamp_seconds(self, timestamp: float) -> "LongPositionVector": + def set_tst_in_normal_timestamp_seconds( + self, timestamp: float + ) -> "LongPositionVector": """ Set the timestamp in normal timestamp format. @@ -294,9 +314,19 @@ def set_tst_in_normal_timestamp_seconds(self, timestamp: float) -> "LongPosition Timestamp in normal timestamp format. """ tst = self.tst.set_in_normal_timestamp_seconds(timestamp) - return LongPositionVector(gn_addr=self.gn_addr, tst=tst, latitude=self.latitude, longitude=self.longitude, pai=self.pai, s=self.s, h=self.h) + return LongPositionVector( + gn_addr=self.gn_addr, + tst=tst, + latitude=self.latitude, + longitude=self.longitude, + pai=self.pai, + s=self.s, + h=self.h, + ) - def set_tst_in_normal_timestamp_milliseconds(self, timestamp: int) -> "LongPositionVector": + def set_tst_in_normal_timestamp_milliseconds( + self, timestamp: int + ) -> "LongPositionVector": """ Set the timestamp in normal timestamp format. @@ -306,7 +336,15 @@ def set_tst_in_normal_timestamp_milliseconds(self, timestamp: int) -> "LongPosit Timestamp in normal timestamp format. """ tst = self.tst.set_in_normal_timestamp_milliseconds(timestamp) - return LongPositionVector(gn_addr=self.gn_addr, tst=tst, latitude=self.latitude, longitude=self.longitude, pai=self.pai, s=self.s, h=self.h) + return LongPositionVector( + gn_addr=self.gn_addr, + tst=tst, + latitude=self.latitude, + longitude=self.longitude, + pai=self.pai, + s=self.s, + h=self.h, + ) def set_latitude(self, latitude: float) -> "LongPositionVector": """ @@ -317,7 +355,15 @@ def set_latitude(self, latitude: float) -> "LongPositionVector": latitude : float Latitude in WGS 84 1/10 micro degree. """ - return LongPositionVector(gn_addr=self.gn_addr, tst=self.tst, latitude=int(latitude * 10000000), longitude=self.longitude, pai=self.pai, s=self.s, h=self.h) + return LongPositionVector( + gn_addr=self.gn_addr, + tst=self.tst, + latitude=int(latitude * 10000000), + longitude=self.longitude, + pai=self.pai, + s=self.s, + h=self.h, + ) def set_longitude(self, longitude: float) -> "LongPositionVector": """ @@ -328,7 +374,15 @@ def set_longitude(self, longitude: float) -> "LongPositionVector": longitude : float Longitude in WGS 84 1/10 micro degree. """ - return LongPositionVector(gn_addr=self.gn_addr, tst=self.tst, latitude=self.latitude, longitude=int(longitude * 10000000), pai=self.pai, s=self.s, h=self.h) + return LongPositionVector( + gn_addr=self.gn_addr, + tst=self.tst, + latitude=self.latitude, + longitude=int(longitude * 10000000), + pai=self.pai, + s=self.s, + h=self.h, + ) def set_pai(self, pai: bool) -> "LongPositionVector": """ @@ -339,7 +393,15 @@ def set_pai(self, pai: bool) -> "LongPositionVector": pai : bool PAI. """ - return LongPositionVector(gn_addr=self.gn_addr, tst=self.tst, latitude=self.latitude, longitude=self.longitude, pai=pai, s=self.s, h=self.h) + return LongPositionVector( + gn_addr=self.gn_addr, + tst=self.tst, + latitude=self.latitude, + longitude=self.longitude, + pai=pai, + s=self.s, + h=self.h, + ) def set_speed(self, speed: float) -> "LongPositionVector": """ @@ -350,7 +412,15 @@ def set_speed(self, speed: float) -> "LongPositionVector": speed : float Speed in m/s. """ - return LongPositionVector(gn_addr=self.gn_addr, tst=self.tst, latitude=self.latitude, longitude=self.longitude, pai=self.pai, s=int(speed * 100), h=self.h) + return LongPositionVector( + gn_addr=self.gn_addr, + tst=self.tst, + latitude=self.latitude, + longitude=self.longitude, + pai=self.pai, + s=int(speed * 100), + h=self.h, + ) def set_heading(self, heading: float) -> "LongPositionVector": """ @@ -361,7 +431,15 @@ def set_heading(self, heading: float) -> "LongPositionVector": heading : float Heading in degree from north. """ - return LongPositionVector(gn_addr=self.gn_addr, tst=self.tst, latitude=self.latitude, longitude=self.longitude, pai=self.pai, s=self.s, h=int(heading * 10)) + return LongPositionVector( + gn_addr=self.gn_addr, + tst=self.tst, + latitude=self.latitude, + longitude=self.longitude, + pai=self.pai, + s=self.s, + h=int(heading * 10), + ) def refresh_with_tpv_data(self, tpv_data: dict) -> "LongPositionVector": """ @@ -379,10 +457,11 @@ def refresh_with_tpv_data(self, tpv_data: dict) -> "LongPositionVector": latitude=int(tpv_data["lat"] * 10**7), longitude=int(tpv_data["lon"] * 10**7), s=int(tpv_data["speed"] * 100), - h=int(tpv_data["track"] * 10) + h=int(tpv_data["track"] * 10), ) lpv = lpv.set_tst_in_normal_timestamp_seconds( - int(parser.parse(tpv_data["time"]).timestamp())) + int(parser.parse(tpv_data["time"]).timestamp()) + ) return lpv def encode(self) -> bytes: @@ -394,9 +473,15 @@ def encode(self) -> bytes: bytes Encoded LongPositionVector. """ - return ((self.gn_addr.encode_to_int() << 32*4) | (self.tst.encode() << 32*3) | ( - self.latitude << 32*2) | (self.longitude << 32) | ( - self.pai << 31) | (self.s << 16) | self.h).to_bytes(24, byteorder='big') + return ( + (self.gn_addr.encode_to_int() << 32 * 4) + | (self.tst.encode() << 32 * 3) + | (self.latitude << 32 * 2) + | (self.longitude << 32) + | (self.pai << 31) + | (self.s << 16) + | self.h + ).to_bytes(24, byteorder="big") def encode_to_int(self) -> int: """ @@ -407,8 +492,15 @@ def encode_to_int(self) -> int: int Encoded LongPositionVector. """ - return (self.gn_addr.encode_to_int() << 32*4) | (self.tst.encode() << 32*3) | ( - self.latitude << 32*2) | (self.longitude << 32) | (int(self.pai) << 31) | (self.s << 16) | self.h + return ( + (self.gn_addr.encode_to_int() << 32 * 4) + | (self.tst.encode() << 32 * 3) + | (self.latitude << 32 * 2) + | (self.longitude << 32) + | (int(self.pai) << 31) + | (self.s << 16) + | self.h + ) @classmethod def decode(cls, data: bytes) -> "LongPositionVector": @@ -422,16 +514,23 @@ def decode(cls, data: bytes) -> "LongPositionVector": """ if len(data) < 24: raise DecodeError("LongPositionVector must be 24 bytes long") - data_as_int = int.from_bytes(data[0:24], byteorder='big') - gn_addr = GNAddress.decode( - (data_as_int >> 32 * 4).to_bytes(8, byteorder='big')) + data_as_int = int.from_bytes(data[0:24], byteorder="big") + gn_addr = GNAddress.decode((data_as_int >> 32 * 4).to_bytes(8, byteorder="big")) tst = TST.decode(data_as_int >> 32 * 3) latitude = (data_as_int >> 32 * 2) & 0xFFFFFFFF longitude = (data_as_int >> 32) & 0xFFFFFFFF pai = bool((data_as_int >> 31) & 0x1) s = (data_as_int >> 16) & 0x7FFF h = data_as_int & 0xFFFF - return cls(gn_addr=gn_addr, tst=tst, latitude=latitude, longitude=longitude, pai=pai, s=s, h=h) + return cls( + gn_addr=gn_addr, + tst=tst, + latitude=latitude, + longitude=longitude, + pai=pai, + s=s, + h=h, + ) def __eq__(self, __o: object) -> bool: if isinstance(__o, LongPositionVector): @@ -447,14 +546,30 @@ def __eq__(self, __o: object) -> bool: return False def __str__(self): - return "LongPositionVector: \n" + \ - " GN Address: " + str(self.gn_addr) + "\n" + \ - " Timestamp: " + str(self.tst) + "\n" + \ - " Latitude: " + str(self.latitude) + "\n" + \ - " Longitude: " + str(self.longitude) + "\n" + \ - " PAI: " + str(self.pai) + "\n" + \ - " Speed: " + str(self.s) + "\n" + \ - " Heading: " + str(self.h) + "\n" + return ( + "LongPositionVector: \n" + + " GN Address: " + + str(self.gn_addr) + + "\n" + + " Timestamp: " + + str(self.tst) + + "\n" + + " Latitude: " + + str(self.latitude) + + "\n" + + " Longitude: " + + str(self.longitude) + + "\n" + + " PAI: " + + str(self.pai) + + "\n" + + " Speed: " + + str(self.s) + + "\n" + + " Heading: " + + str(self.h) + + "\n" + ) @dataclass(frozen=True) @@ -488,9 +603,16 @@ def set_gn_addr(self, gn_addr: GNAddress) -> "ShortPositionVector": gn_addr : GNAddress GN address. """ - return ShortPositionVector(gn_addr=gn_addr, tst=self.tst, latitude=self.latitude, longitude=self.longitude) + return ShortPositionVector( + gn_addr=gn_addr, + tst=self.tst, + latitude=self.latitude, + longitude=self.longitude, + ) - def set_tst_in_normal_timestamp_seconds(self, timestamp: int) -> "ShortPositionVector": + def set_tst_in_normal_timestamp_seconds( + self, timestamp: int + ) -> "ShortPositionVector": """ Set the timestamp in normal timestamp format. @@ -500,9 +622,16 @@ def set_tst_in_normal_timestamp_seconds(self, timestamp: int) -> "ShortPositionV Timestamp in normal timestamp format. """ tst = self.tst.set_in_normal_timestamp_seconds(timestamp) - return ShortPositionVector(gn_addr=self.gn_addr, tst=tst, latitude=self.latitude, longitude=self.longitude) + return ShortPositionVector( + gn_addr=self.gn_addr, + tst=tst, + latitude=self.latitude, + longitude=self.longitude, + ) - def set_tst_in_normal_timestamp_milliseconds(self, timestamp: int) -> "ShortPositionVector": + def set_tst_in_normal_timestamp_milliseconds( + self, timestamp: int + ) -> "ShortPositionVector": """ Set the timestamp in normal timestamp format. @@ -512,7 +641,12 @@ def set_tst_in_normal_timestamp_milliseconds(self, timestamp: int) -> "ShortPosi Timestamp in normal timestamp format. """ tst = self.tst.set_in_normal_timestamp_milliseconds(timestamp) - return ShortPositionVector(gn_addr=self.gn_addr, tst=tst, latitude=self.latitude, longitude=self.longitude) + return ShortPositionVector( + gn_addr=self.gn_addr, + tst=tst, + latitude=self.latitude, + longitude=self.longitude, + ) def set_latitude(self, latitude: float) -> "ShortPositionVector": """ @@ -523,7 +657,12 @@ def set_latitude(self, latitude: float) -> "ShortPositionVector": latitude : float Latitude in WGS 84 1/10 micro degree. """ - return ShortPositionVector(gn_addr=self.gn_addr, tst=self.tst, latitude=int(latitude * 10000000), longitude=self.longitude) + return ShortPositionVector( + gn_addr=self.gn_addr, + tst=self.tst, + latitude=int(latitude * 10000000), + longitude=self.longitude, + ) def get_latitude(self) -> float: """ @@ -534,7 +673,7 @@ def get_latitude(self) -> float: float Latitude in WGS 84 1/10 micro degree. """ - return self.latitude/10000000 + return self.latitude / 10000000 def set_longitude(self, longitude: float) -> "ShortPositionVector": """ @@ -545,7 +684,12 @@ def set_longitude(self, longitude: float) -> "ShortPositionVector": longitude : float Longitude in WGS 84 1/10 micro degree. """ - return ShortPositionVector(gn_addr=self.gn_addr, tst=self.tst, latitude=self.latitude, longitude=int(longitude * 10000000)) + return ShortPositionVector( + gn_addr=self.gn_addr, + tst=self.tst, + latitude=self.latitude, + longitude=int(longitude * 10000000), + ) def get_longitude(self) -> float: """ @@ -556,7 +700,7 @@ def get_longitude(self) -> float: float Longitude in WGS 84 1/10 micro degree. """ - return self.longitude/10000000 + return self.longitude / 10000000 def encode(self) -> bytes: """ @@ -567,8 +711,12 @@ def encode(self) -> bytes: bytes Encoded ShortPositionVector. """ - return ((self.gn_addr.encode_to_int() << 32*3) | (self.tst.encode() << 32*2) | ( - self.latitude << 32*1) | self.longitude).to_bytes(20, byteorder='big') + return ( + (self.gn_addr.encode_to_int() << 32 * 3) + | (self.tst.encode() << 32 * 2) + | (self.latitude << 32 * 1) + | self.longitude + ).to_bytes(20, byteorder="big") def encode_to_int(self) -> int: """ @@ -579,8 +727,12 @@ def encode_to_int(self) -> int: int Encoded ShortPositionVector. """ - return (self.gn_addr.encode_to_int() << 32*3) | (self.tst.encode() << 32*2) | ( - self.latitude << 32*1) | self.longitude + return ( + (self.gn_addr.encode_to_int() << 32 * 3) + | (self.tst.encode() << 32 * 2) + | (self.latitude << 32 * 1) + | self.longitude + ) @classmethod def decode(cls, data: bytes) -> "ShortPositionVector": @@ -592,9 +744,8 @@ def decode(cls, data: bytes) -> "ShortPositionVector": data : bytes Encoded ShortPositionVector. """ - data_int = int.from_bytes(data, byteorder='big') - gn_addr = GNAddress.decode( - (data_int >> 32 * 3).to_bytes(8, byteorder='big')) + data_int = int.from_bytes(data, byteorder="big") + gn_addr = GNAddress.decode((data_int >> 32 * 3).to_bytes(8, byteorder="big")) tst = TST.decode(data_int >> 32 * 2) latitude = (data_int >> 32 * 1) & 0xFFFFFFFF longitude = data_int & 0xFFFFFFFF diff --git a/src/flexstack/geonet/router.py b/src/flexstack/geonet/router.py index 9afb339..a5bafad 100644 --- a/src/flexstack/geonet/router.py +++ b/src/flexstack/geonet/router.py @@ -1,7 +1,8 @@ from __future__ import annotations from collections.abc import Callable from enum import Enum -from threading import Lock +from time import sleep +from threading import Thread, Lock import math from ..linklayer.exceptions import ( SendingException, @@ -91,6 +92,54 @@ def __init__(self, mib: MIB, sign_service: SignService | None = None) -> None: self.indication_callback = None self.sequence_number_lock = Lock() self.sequence_number = 0 + if self.mib.itsGnBeaconServiceRetransmitTimer > 0: + self.configure_beacon_service() + + def configure_beacon_service(self) -> None: + """ + Configures, set-ups threading, for the beacon service based on MIB settings. + + Parameters + ---------- + None + """ + + thread = Thread(target=self.beacon_service_thread, daemon=True) + thread.start() + + def beacon_service_thread(self) -> None: + """ + Thread function to handle beacon service retransmissions. + """ + + while True: + self.gn_data_request_beacon() + sleep(self.mib.itsGnBeaconServiceRetransmitTimer / 1000) + + def gn_data_request_beacon(self) -> None: + """ + Handle a Beacon GNDataRequest. + + Parameters + ---------- + None + """ + basic_header = BasicHeader.initialize_with_mib_and_rhl(self.mib, 1) + common_header = CommonHeader.initialize_beacon() + long_position_vector = self.ego_position_vector + packet = ( + basic_header.encode_to_bytes() + + common_header.encode_to_bytes() + + long_position_vector.encode() + ) + + try: + if self.link_layer: + self.link_layer.send(packet) + except PacketTooLongException: + pass + except SendingException: + pass def get_sequence_number(self) -> int: """ @@ -571,6 +620,36 @@ def gn_data_indicate_gbc( print(str(e)) return GNDataIndication() + def gn_data_indicate_beacon(self, packet: bytes) -> None: + """ + Method to indicate a Beacon GeoNetworking packet. + + Lower level layers should call this method to indicate a Beacon GeoNetworking packet. + + Parameters + ---------- + packet : bytes + GeoNetworking packet to indicate. + common_header : CommonHeader + CommonHeader of the packet. + """ + # ETSI EN 302 636-4-1 V1.4.1 (2020-01). Section 10.3.6 + try: + long_position_vector = LongPositionVector() + long_position_vector.decode(packet[0:24]) + packet = packet[24:] + # Receiver operations of Beacon packets are identical to the + # handling procedures of the SHB packet (clause 10.3.10.3) + self.location_table.new_shb_packet(long_position_vector, packet) + except DADException: + print("Duplicate Address Detected!") + except IncongruentTimestampException: + print("Incongruent Timestamp Detected!") + except DuplicatedPacketException: + print("Packet is duplicated") + except DecodeError as e: + print(str(e)) + def gn_data_indicate(self, packet: bytes) -> None: # pylint: disable=no-else-raise, too-many-branches """ @@ -606,7 +685,8 @@ def gn_data_indicate(self, packet: bytes) -> None: raise NotImplementedError( "Any packet (Common Header) not implemented") elif common_header.ht == HeaderType.BEACON: - raise NotImplementedError("Beacon not implemented") + self.gn_data_indicate_beacon(packet) + return elif common_header.ht == HeaderType.GEOUNICAST: raise NotImplementedError("Geounicast not implemented") elif common_header.ht == HeaderType.GEOANYCAST: diff --git a/tests/flexstack/geonet/test_gn_address.py b/tests/flexstack/geonet/test_gn_address.py index 0d92832..f02c1fa 100644 --- a/tests/flexstack/geonet/test_gn_address.py +++ b/tests/flexstack/geonet/test_gn_address.py @@ -12,7 +12,7 @@ def test_encode_to_address(self): class TestST(unittest.TestCase): def test_encode_to_address(self): self.assertEqual(ST(0).encode_to_address(), 0) - self.assertEqual(ST(1).encode_to_address(), 0x0800000000000000) + self.assertEqual(ST(1).encode_to_address(), 0x0400000000000000) class TestMID(unittest.TestCase): @@ -36,7 +36,7 @@ def test_encode_to_address(self): mid=MID(b'\xaa\xbb\xcc\x11\x22\x33') ) self.assertEqual(gn_address.encode(), - b'\x88\x00\xaa\xbb\xcc\x11\x22\x33') + b'\x84\x00\xaa\xbb\xcc\x11\x22\x33') def test_decode(self): gn_address = GNAddress( diff --git a/tests/flexstack/geonet/test_position_vector.py b/tests/flexstack/geonet/test_position_vector.py index 44b7db9..080499e 100644 --- a/tests/flexstack/geonet/test_position_vector.py +++ b/tests/flexstack/geonet/test_position_vector.py @@ -41,7 +41,7 @@ def test_encode(self): ) lpv = lpv.set_tst_in_normal_timestamp_seconds(1674638854) self.assertEqual(lpv.encode( - ), bytes.fromhex('8800aabbcc112233198662f81f4dead007fd6f0480000000')) + ), bytes.fromhex('8400aabbcc112233198662f81f4dead007fd6f0480000000')) def test_decode(self): lpv = LongPositionVector.decode( @@ -69,7 +69,7 @@ def test_encode(self): ) spv = spv.set_tst_in_normal_timestamp_seconds(1674638854) self.assertEqual(spv.encode( - ), b'\x88\x00\xaa\xbb\xcc\x11"3\x19\x86b\xf8\x1fM\xea\xd0\x07\xfdo\x04') + ), b'\x84\x00\xaa\xbb\xcc\x11"3\x19\x86b\xf8\x1fM\xea\xd0\x07\xfdo\x04') def test_decode(self): spv = ShortPositionVector.decode(