-module(otc_mtp3). -behaviour(otc_codec). -export([spec/0, codec/1, next/1, decode/1, encode/1 ]). -include("include/mtp3.hrl"). spec() -> "ITU-T Q.704 (07/96) July 1996". codec(Bin) when is_binary(Bin) -> case decode(Bin) of #{payload := D} = Msg when is_binary(D) -> {maps:without([payload], Msg), D}; Msg -> Msg end; codec(Map) when is_map(Map) -> encode(Map). -type subproto() :: sccp | tup | isup | dup_call | dup_reg | mtp_test | broadband_isup | satellite_isup. -spec next(map()) -> '$stop' | {ok, subproto()}. next(#{service_indicator := SI} = V) when not is_map_key(payload, V) -> {ok, SI}; next(_) -> '$stop'. decode(<>) -> NetworkInd = parse_network_indicator(NI), ServiceInd = parse_service_indicator(SI), %% Routing Label is in reverse byte order RL = <>, <> = RL, Msg = decode_msg(ServiceInd, Rest), Msg#{network_indicator => NetworkInd, national_use_spare => NU, service_indicator => ServiceInd, signalling_link_selection => SLS, origin_point_code => OPC, destination_point_code => DPC }. encode(#{network_indicator := NetworkInd, service_indicator := ServiceInd} = Msg) -> #{signalling_link_selection := SLS, origin_point_code := OPC, destination_point_code := DPC } = Msg, NI = compose_network_indicator(NetworkInd), NU = maps:get(national_use_spare, Msg, 2#00), SI = compose_service_indicator(ServiceInd), RL0 = <>, %% Routing Label is in reverse byte order <> = RL0, Bin = encode_msg(ServiceInd, Msg), <>. parse_service_indicator(?MTP3_SERVIND_MGMT) -> mgmt; parse_service_indicator(?MTP3_SERVIND_MAINT) -> maint; parse_service_indicator(?MTP3_SERVIND_SCCP) -> sccp; parse_service_indicator(?MTP3_SERVIND_TUP) -> tup; parse_service_indicator(?MTP3_SERVIND_ISUP) -> isup; parse_service_indicator(?MTP3_SERVIND_DUP_CALL) -> dup_call; parse_service_indicator(?MTP3_SERVIND_DUP_REG) -> dup_reg; parse_service_indicator(?MTP3_SERVIND_MTP_TEST) -> mtp_test; parse_service_indicator(?MTP3_SERVIND_BROADBAND_ISUP) -> broadband_isup; parse_service_indicator(?MTP3_SERVIND_SATELLITE_ISUP) -> satellite_isup. compose_service_indicator(mgmt) -> ?MTP3_SERVIND_MGMT; compose_service_indicator(maint) -> ?MTP3_SERVIND_MAINT; compose_service_indicator(sccp) -> ?MTP3_SERVIND_SCCP; compose_service_indicator(tup) -> ?MTP3_SERVIND_TUP; compose_service_indicator(isup) -> ?MTP3_SERVIND_ISUP; compose_service_indicator(dup_call) -> ?MTP3_SERVIND_DUP_CALL; compose_service_indicator(dup_reg) -> ?MTP3_SERVIND_DUP_REG; compose_service_indicator(mtp_test) -> ?MTP3_SERVIND_MTP_TEST; compose_service_indicator(broadband_isup) -> ?MTP3_SERVIND_BROADBAND_ISUP; compose_service_indicator(satellite_isup) -> ?MTP3_SERVIND_SATELLITE_ISUP. parse_network_indicator(?MTP3_NETIND_INTERNATIONAL) -> international; parse_network_indicator(?MTP3_NETIND_INTERNATIONAL_SPARE) -> international_spare; parse_network_indicator(?MTP3_NETIND_NATIONAL) -> national; parse_network_indicator(?MTP3_NETIND_NATIONAL_SPARE) -> national_spare. compose_network_indicator(international) -> ?MTP3_NETIND_INTERNATIONAL; compose_network_indicator(international_spare) -> ?MTP3_NETIND_INTERNATIONAL_SPARE; compose_network_indicator(national) -> ?MTP3_NETIND_NATIONAL; compose_network_indicator(national_spare) -> ?MTP3_NETIND_NATIONAL_SPARE. decode_msg(mgmt, Bin) -> %% Q.704 #{payload => decode_mgmt(Bin)}; decode_msg(maint, Bin) -> %% Q.707 #{payload => decode_maint(Bin)}; decode_msg(_, Bin) -> #{payload => Bin}. encode_msg(mgmt, #{payload := Msg}) -> %% Q.704 encode_mgmt(Msg); encode_msg(maint, #{payload := Msg}) -> %% Q.707 encode_maint(Msg); encode_msg(_, #{payload := Bin}) -> Bin. decode_mgmt(<>) -> <> = Bin, #{message_type => changeover_order, forward_sequence_number => FSN}; decode_mgmt(<>) -> <> = Bin, #{message_type => changeover_ack, forward_sequence_number => FSN}; decode_mgmt(<>) -> <> = Bin, #{message_type => extended_changeover_order, forward_sequence_number => FSN}; decode_mgmt(<>) -> <> = Bin, #{message_type => extended_changeover_ack, forward_sequence_number => FSN}; decode_mgmt(<>) -> <> = Bin, #{message_type => changeback_declaration, changeback_code => CBC}; decode_mgmt(<>) -> <> = Bin, #{message_type => changeback_ack, changeback_code => CBC}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => emergency_changeover}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => emergency_changeover_ack}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => route_congestion_test}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => transfer_controlled}; decode_mgmt(<>) -> <> = Bin, #{message_type => transfer_prohibited, destination_point_code => DPC}; decode_mgmt(<>) -> <> = Bin, #{message_type => transfer_restricted, destination_point_code => DPC}; decode_mgmt(<>) -> <> = Bin, #{message_type => transfer_allowed, destination_point_code => DPC}; decode_mgmt(<>) -> <> = Bin, #{message_type => route_set_test_prohibited, destination_point_code => DPC}; decode_mgmt(<>) -> <> = Bin, #{message_type => route_set_test_restricted, destination_point_code => DPC}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => link_inhibit}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => link_uninhibit}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => link_inhibit_ack}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => link_uninhibit_ack}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => link_inhibit_denied}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => link_force_uninhibit}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => link_local_inhibit_test}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => link_remote_inhibit_test}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => traffic_restart_allowed}; decode_mgmt(<>) -> <> = Bin, #{message_type => data_link_connection_order, signalling_data_link_id => SDLID}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => connection_successful}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => connection_not_successful}; decode_mgmt(<>) -> <<>> = Bin, #{message_type => connection_not_possible}; decode_mgmt(<>) -> <> = Bin, UnavailabilityCause = parse_unavailability_cause(UC), #{message_type => user_part_unavailable, destination_point_code => DPC, user_part_id => UPID, unavailability_cause => UnavailabilityCause }. encode_mgmt(#{message_type := changeover_order, forward_sequence_number := FSN}) -> Bin = <>, <>; encode_mgmt(#{message_type := changeover_ack, forward_sequence_number := FSN}) -> Bin = <>, <>; encode_mgmt(#{message_type := extended_changeover_order, forward_sequence_number := FSN}) -> Bin = <>, <>; encode_mgmt(#{message_type := extended_changeover_ack, forward_sequence_number := FSN}) -> Bin = <>, <>; encode_mgmt(#{message_type := changeback_declaration, changeback_code := CBC}) -> Bin = <>, <>; encode_mgmt(#{message_type := changeback_ack, changeback_code := CBC}) -> Bin = <>, <>; encode_mgmt(#{message_type := emergency_changeover}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := emergency_changeover_ack}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := route_congestion_test}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := transfer_controlled}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := transfer_prohibited, destination_point_code := DPC}) -> Bin = <>, <>; encode_mgmt(#{message_type := transfer_restricted, destination_point_code := DPC}) -> Bin = <>, <>; encode_mgmt(#{message_type := transfer_allowed, destination_point_code := DPC}) -> Bin = <>, <>; encode_mgmt(#{message_type := route_set_test_prohibited, destination_point_code := DPC}) -> Bin = <>, <>; encode_mgmt(#{message_type := route_set_test_restricted, destination_point_code := DPC}) -> Bin = <>, <>; encode_mgmt(#{message_type := link_inhibit}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := link_uninhibit}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := link_inhibit_ack}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := link_uninhibit_ack}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := link_inhibit_denied}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := link_force_uninhibit}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := link_local_inhibit_test}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := link_remote_inhibit_test}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := traffic_restart_allowed}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := data_link_connection_order, signalling_data_link_id := SDLID}) -> Bin = <>, <>; encode_mgmt(#{message_type := connection_successful}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := connection_not_successful}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := connection_not_possible}) -> Bin = <<>>, <>; encode_mgmt(#{message_type := user_part_unavailable, destination_point_code := DPC, user_part_id := UPID, unavailability_cause := UnavailabilityCause }) -> UC = compose_unavailability_cause(UnavailabilityCause), Bin = <>, <>. parse_unavailability_cause(?MTP3_UPU_CAUSE_UNKNOWN) -> unknown; parse_unavailability_cause(?MTP3_UPU_CAUSE_UNEQUIPPED) -> unequipped; parse_unavailability_cause(?MTP3_UPU_CAUSE_INACCESSIBLE) -> inaccessible. compose_unavailability_cause(unknown) -> ?MTP3_UPU_CAUSE_UNKNOWN; compose_unavailability_cause(unequipped) -> ?MTP3_UPU_CAUSE_UNEQUIPPED; compose_unavailability_cause(inaccessible) -> ?MTP3_UPU_CAUSE_INACCESSIBLE. decode_maint(<>) -> <> = Bin, <> = Rest, #{message_type => signalling_link_test, test_pattern => TP}; decode_maint(<>) -> <> = Bin, <> = Rest, #{message_type => signalling_link_test_ack, test_pattern => TP}. encode_maint(#{message_type := signalling_link_test, test_pattern := TP}) -> L = byte_size(TP), Bin = <>, <>; encode_maint(#{message_type := signalling_link_test_ack, test_pattern := TP}) -> L = byte_size(TP), Bin = <>, <>.