lib/aixm/component/service.rb

Summary

Maintainability
A
0 mins
Test Coverage
using AIXM::Refinements

module AIXM
  class Component

    # Service provided by a unit.
    #
    # ===Cheat Sheet in Pseudo Code:
    #   service = AIXM.service(
    #     type: TYPES
    #   )
    #   service.timetable = AIXM.timetable or nil
    #   service.remarks = String or nil
    #   service.add_frequency(AIXM.frequency)
    #
    # @see https://gitlab.com/openflightmaps/ofmx/wikis/Organisation#ser-service
    class Service < Component
      include AIXM::Concerns::Association
      include AIXM::Concerns::Timetable
      include AIXM::Concerns::Remarks

      TYPES = {
        ACS: :area_control_service,
        ADS: :automatic_dependent_surveillance_service,
        ADVS: :advisory_service,
        AFIS: :aerodrome_flight_information_service,
        AFS: :aeronautical_fixed_service,
        AIS: :aeronautical_information_service,
        ALRS: :alerting_service,
        AMS: :aeronautical_mobile_service,
        AMSS: :aeronautical_mobile_satellite_service,
        APP: :approach_control_service,
        'APP-ARR': :approach_control_service_for_arrival,
        'APP-DEP': :approach_control_service_for_departure,
        ARTCC: :air_route_traffic_control_centre_service,
        ATC: :air_traffic_control_service,
        ATFM: :air_traffic_flow_management_service,
        ATIS: :automated_terminal_information_service,
        'ATIS-ARR': :automated_terminal_information_service_for_arrival,
        'ATIS-DEP': :automated_terminal_information_service_for_departure,
        ATM: :air_traffic_management_service,
        ATS: :air_traffic_service,
        BOF: :briefing_service,
        BS: :commercial_broadcasting_service,
        COM: :communications_service,
        CTAF: :common_traffic_advisory_frequency_service,
        DVDF: :doppler_vdf_service,
        EFAS: :en_route_flight_advisory_service,
        ENTRY: :entry_clearance_service,
        EXIT: :exit_clearance_service,
        FCST: :forecasting_service,
        FIS: :flight_information_service,
        FISA: :automated_flight_information_service,
        FSS: :flight_service_station_service,
        GCA: :ground_controlled_approach_service,
        INFO: :information_provision_service,
        MET: :meteorological_service,
        NOF: :international_notam_service,
        OAC: :oceanic_area_control_service,
        OVERFLT: :overflight_clearance_service,
        PAR: :precision_approach_radar_service,
        RAC: :rules_of_the_air_and_air_traffic_services,
        RADAR: :radar_service,
        RAF: :regional_area_forecasting_service,
        RCC: :rescue_coordination_service,
        SAR: :search_and_rescue_service,
        SIGMET: :sigmet_service,
        SMC: :surface_movement_control_service,
        SMR: :surface_movement_radar_service,
        SRA: :surveillance_radar_approach_service,
        SSR: :secondary_surveillance_radar_service,
        TAR: :terminal_area_radar_service,
        TWEB: :transcribed_weather_broadcast_service,
        TWR: :aerodrome_control_tower_service,
        UAC: :upper_area_control_service,
        UDF: :uhf_direction_finding_service,
        VDF: :vdf_direction_finding_service,
        VOLMET: :volmet_service,
        VOT: :vor_test_facility,
        OTHER: :other   # specify in remarks
      }.freeze

      # Map service types to guessed unit types
      GUESSED_UNIT_TYPES_MAP = {
        :advisory_service => :advisory_centre,
        :aerodrome_control_tower_service => :aerodrome_control_tower,
        :aerodrome_flight_information_service => :aerodrome_control_tower,
        :aeronautical_information_service => :aeronautical_information_services_office,
        :air_route_traffic_control_centre_service => :air_route_traffic_control_centre,
        :air_traffic_control_service => :air_traffic_control_centre,
        :air_traffic_flow_management_service => :air_traffic_flow_management_unit,
        :air_traffic_management_service => :air_traffic_management_unit,
        :air_traffic_service => :air_traffic_services_unit,
        :approach_control_service => :approach_control_office,
        :approach_control_service_for_arrival => :arrivals_approach_control_office,
        :approach_control_service_for_departure => :depatures_approach_control_office,
        :area_control_service => :area_control_centre,
        :automated_terminal_information_service => :aerodrome_control_tower,
        :automated_terminal_information_service_for_arrival => :aerodrome_control_tower,
        :automated_terminal_information_service_for_departure => :aerodrome_control_tower,
        :automatic_dependent_surveillance_service => :automatic_dependent_surveillance_unit,
        :briefing_service => :briefing_office,
        :commercial_broadcasting_service => :commercial_broadcasting_station,
        :communications_service => :communications_office,
        :flight_information_service => :flight_information_centre,
        :flight_service_station_service => :flight_service_station,
        :forecasting_service => :forecasting_office,
        :ground_controlled_approach_service => :ground_controlled_approach_systems_office,
        :international_notam_service => :international_notam_office,
        :meteorological_service => :meteorological_office,
        :oceanic_area_control_service => :oceanic_control_centre,
        :precision_approach_radar_service => :precision_approach_radar_centre,
        :radar_service => :radar_office,
        :regional_area_forecasting_service => :regional_area_forecast_centre,
        :rescue_coordination_service => :rescue_coordination_centre,
        :search_and_rescue_service => :search_and_rescue_centre,
        :secondary_surveillance_radar_service => :secondary_surveillance_radar_centre,
        :sigmet_service => :meteorological_office,
        :surface_movement_control_service => :surface_movement_control_office,
        :surface_movement_radar_service => :surface_movement_radar_office,
        :surveillance_radar_approach_service => :surveillance_radar_approach_centre,
        :terminal_area_radar_service => :terminal_area_surveillance_radar_centre,
        :transcribed_weather_broadcast_service => :meteorological_office,
        :uhf_direction_finding_service => :uhf_direction_finding_station,
        :upper_area_control_service => :upper_area_control_centre,
        :vdf_direction_finding_service => :vdf_direction_finding_station,
        :volmet_service => :meteorological_office,
        :other => :other
      }.freeze

      # @!method frequencies
      #   @return [Array<AIXM::Component::Frequency>] frequencies used by this service
      #
      # @!method add_frequency(frequency)
      #   @param frequency [AIXM::Component::Frequency]
      has_many :frequencies

      # @!method unit
      #   @return [AIXM::Feature::Unit] unit providing this service
      belongs_to :unit

      # @!method airport
      #   @return [AIXM::Feature::Airport] airport this service is provided at
      belongs_to :airport

      # @!method airspace
      #   @return [AIXM::Feature::Airspace] airspace this service is provided in
      belongs_to :airspace

      # @!method layer
      #   @return [AIXM::Component::Layer] airspace layer this service is provided within
      belongs_to :layer

      # Type of service
      #
      # @overload type
      #   @return [Symbol] any of {TYPES}
      # @overload type=(value)
      #   @param value [Symbol] any of {TYPES}
      attr_reader :type

      # See the {cheat sheet}[AIXM::Component::Service] for examples on how to
      # create instances of this class.
      def initialize(type:)
        self.type = type
        @sequence = 1
      end

      # @return [String]
      def inspect
        %Q(#<#{self.class} type=#{type.inspect}>)
      end

      def type=(value)
        @type = TYPES.lookup(value&.to_s&.to_sym, nil) || fail(ArgumentError, "invalid type")
      end

      # Guess the unit type for this service
      #
      # @return [Symbol, nil] guessed unit type or +nil+ if unmappable
      def guessed_unit_type
        GUESSED_UNIT_TYPES_MAP[type]
      end

      # @!visibility private
      def add_uid_to(builder)
        resequence!
        builder.SerUid do |ser_uid|
          unit.add_uid_to(ser_uid)
          ser_uid.codeType(TYPES.key(type))
          ser_uid.noSeq(@sequence)
        end
      end

      # @!visibility private
      def add_to(builder)
        builder.comment ["Service: #{TYPES.key(type)}", unit&.send(:name_with_type)].compact.join(' by ').dress
        builder.text "\n"
        builder.Ser do |ser|
          add_uid_to(ser)
          timetable.add_to(ser, as: :Stt) if timetable
          ser.txtRmk(remarks) if remarks
        end
        frequencies.each do |frequency|
          frequency.add_to(builder)
        end
      end

      private

      def resequence!
        unit.services.sort { |a, b| a.type <=> b.type }.each.with_object({}) do |service, sequences|
          sequences[service.type] = (sequences[service.type] || 0) + 1
          service.instance_variable_set(:@sequence, sequences[service.type])
        end
      end

    end

  end
end