TopologyRequest syntax error

Hakan Mattsson <>
Thu Sep 12 14:43:52 CEST 2002


On Thu, 12 Sep 2002, Peter-Henry Mander wrote:

erlang>I'm using the Megaco stack in Erlang/OTP reusing the simple MGC and 
erlang>simple MG code, and I'm having a problem with the TopologyRequest issued 
erlang>from the Gateway Controller. The Erlang code used to add a 
erlang>TopologyRequest is included below. The Controller issues the 
erlang>ActionRequest without complaining, but the Gateway at the other end 
erlang>gives a syntax error, correctly I believe since the Controller issues 
erlang>a TopologyRequest within a TopologyRequest, and that doesn't look right 
erlang>to me!
erlang>
erlang>What am I doing wrong?

Congratulations, you seem to be the first one that have decoded
textual topology requests!

There was a bug in megaco/src/text/megaco_text_gen.hrl.

I have fixed the bug in the attached file.
It will be included in next snapshot for Erlang/OTP R9.

Replace the old version of the file and re-run make in
megaco/src/text.

/Håkan
-------------- next part --------------
%% ``The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved via the world wide web at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
%% AB. All Rights Reserved.''
%% 
%%     $Id$
%%
%%----------------------------------------------------------------------
%% Purpose: Encode Megaco/H.248 text messages from internal form
%%----------------------------------------------------------------------

-define(META_ENC(Type, Item), Item) .
%% -define(META_ENC(Type, Item), megaco_meta_package:encode(text, Type, Item)).
%% -define(META_DEC(Type, Item), megaco_meta_package:decode(text, Type, Item)).

enc_MegacoMessage(Val) ->
    State = ?INIT_INDENT,
    enc_MegacoMessage(Val, State).

enc_MegacoMessage(Val, State)
  when record(Val, 'MegacoMessage') ->
    [
     ?LWSP,
     enc_AuthenticationHeader(Val#'MegacoMessage'.authHeader, State),
     enc_Message( Val#'MegacoMessage'.mess, State)
    ].

enc_Transaction(Val) ->
    State = ?INIT_INDENT,
    enc_Transaction(Val, State).

enc_CommandRequest(Val) ->
    State = ?INIT_INDENT,
    enc_CommandRequest(Val, State).

enc_ActionReply(Val) ->
    State = ?INIT_INDENT,
    enc_ActionReply(Val, State).

enc_AuthenticationHeader(asn1_NOVALUE, _State) ->
    [];
enc_AuthenticationHeader(Val, State)
  when record(Val, 'AuthenticationHeader') ->
    [
     ?AuthToken,
     ?EQUAL,
     enc_SecurityParmIndex(Val#'AuthenticationHeader'.secParmIndex, State),
     ?COLON,
     enc_SequenceNum(Val#'AuthenticationHeader'.seqNum, State),
     ?COLON,
     enc_AuthData(Val#'AuthenticationHeader'.ad, State),
     ?SEP_INDENT(State)
    ].

enc_SecurityParmIndex({'SecurityParmIndex',Val}, State) ->
    enc_SecurityParmIndex(Val, State);
enc_SecurityParmIndex(Val, State) ->
    [
     "0x",
     enc_HEXDIG(Val, State, 8, 8)
    ].

enc_SequenceNum({'SequenceNum',Val}, State) ->
    enc_SequenceNum(Val, State);
enc_SequenceNum(Val, State) ->
    [
     "0x",
     enc_HEXDIG(Val, State, 8, 8)
    ].

enc_AuthData({'AuthData',Val}, State) ->
    enc_AuthData(Val, State);
enc_AuthData(Val, State) ->
    [
     "0x",
     enc_HEXDIG(Val, State, 32, 64)
    ].

enc_Message(Val, State)
  when record(Val, 'Message') ->
    [
     ?MegacopToken,
     ?SLASH,
     enc_version(Val#'Message'.version, State),
     ?SEP,
     enc_MId(Val#'Message'.mId, State),
     ?SEP_INDENT(State),
     enc_Message_messageBody(Val#'Message'.messageBody, State)
    ].

enc_version(Val, State) when integer(Val), Val >= 0 ->
    enc_DIGIT(Val, State, 0, 99).

enc_Message_messageBody({'Message_messageBody',Val}, State) ->
    enc_Message_messageBody(Val, State);
enc_Message_messageBody({Tag, Val}, State) ->
    case Tag of
	messageError ->
	    enc_ErrorDescriptor(Val, State);
	transactions ->
	    enc_Message_messageBody_transactions(Val, State)
    end.

enc_Message_messageBody_transactions({'Message_messageBody_transactions',Val}, State) ->
    enc_Message_messageBody_transactions(Val, State);
enc_Message_messageBody_transactions(Val, State)
  when list(Val), Val /= []->
    [enc_Transaction(T, State) || T <- Val].

enc_MId({'MId',Val}, State) ->
    enc_MId(Val, State);
enc_MId({Tag, Val}, State) ->
     case Tag of
	 ip4Address ->
	     enc_IP4Address(Val, State);
	 ip6Address ->
	     enc_IP6Address(Val, State);
	 domainName ->
	     enc_DomainName(Val, State);
	 deviceName ->
	     enc_PathName(Val, State);
	 mtpAddress ->
	     enc_mtpAddress(Val, State)
     end.

enc_mtpAddress(Val, State) ->
    [
     ?MtpToken,
     ?LBRKT,
     enc_OCTET_STRING(Val, State, 2, 4),
     ?RBRKT
    ].

enc_DomainName(Val, State)
  when record(Val, 'DomainName') ->
    [
     $<,
     %% BUGBUG: (ALPHA / DIGIT) *63(ALPHA / DIGIT / "-" / ".")
     enc_STRING(Val#'DomainName'.name, State, 1, 64),
     $>,
     case Val#'DomainName'.portNumber of
	 asn1_NOVALUE ->
	     [];
	 PortNumber ->
	     [
	      $:,
	      enc_portNumber(PortNumber, State)
	     ]
     end
    ].

enc_IP4Address(Val, State)
  when record(Val, 'IP4Address') ->
    [A1, A2, A3, A4] = Val#'IP4Address'.address,
    [
     $[,
     enc_V4hex(A1, State),
     ?DOT,
     enc_V4hex(A2, State),
     ?DOT,
     enc_V4hex(A3, State),
     ?DOT,
     enc_V4hex(A4, State),    
     $],
     case Val#'IP4Address'.portNumber of
	 asn1_NOVALUE ->
	     [];
	 PortNumber ->
	     [
	      $:,
	      enc_portNumber(PortNumber, State)
	     ]
     end
    ].

enc_V4hex(Val, State) ->
    enc_DIGIT(Val, State, 0, 255).

enc_IP6Address(Val, State)
  when record(Val, 'IP6Address'),
       list(Val#'IP6Address'.address),
       length(Val#'IP6Address'.address) == 16 ->
    exit(ipv6_not_supported), %% BUGBUG: nyi
    [
     $[,
     Val#'IP6Address'.address,
     $],
     case Val#'IP6Address'.portNumber of
	 asn1_NOVALUE ->
	     [];
	 PortNumber ->
	     [
	      $:,
	      enc_portNumber(PortNumber, State)
	     ]
     end
    ].

enc_PathName({'PathName',Val}, State) ->
    enc_PathName(Val, State);
enc_PathName(Val, State) ->
    %% BUGBUG: ["*"] NAME *("/" / "*"/ ALPHA / DIGIT /"_" / "$" ) 
    %% BUGBUG: ["@" pathDomainName ]
    enc_STRING(Val, State, 1, 64).

enc_Transaction({'Transaction',Val}, State) ->
    enc_Transaction(Val, State);
enc_Transaction({Tag, Val}, State) ->
    case Tag of
	transactionRequest ->
	    enc_TransactionRequest(Val, State);
	transactionPending ->
	    enc_TransactionPending(Val, State);
	transactionReply ->
	    enc_TransactionReply(Val, State);
	transactionResponseAck ->
	    [enc_TransactionAck(T, State) || T <- Val]
    end.

enc_TransactionAck(Val, State)
  when record(Val, 'TransactionAck') ->
    [
     ?ResponseAckToken,
     ?LBRKT_INDENT(State),
     enc_TransactionId(Val#'TransactionAck'.firstAck, ?INC_INDENT(State)),
     case Val#'TransactionAck'.lastAck of
	 asn1_NOVALUE ->
	     [];
	 LastAck ->
	     enc_TransactionId(LastAck, State)
     end,
     ?RBRKT_INDENT(State)
    ].

enc_TransactionId({'TransactionId',Val}, State) ->
    enc_TransactionId(Val, State);
enc_TransactionId(Val, State) ->
    enc_UINT32(Val, State).

enc_TransactionRequest(Val, State)
  when record(Val, 'TransactionRequest') ->
    [
     ?TransToken,
     ?EQUAL,
     enc_TransactionId(Val#'TransactionRequest'.transactionId, State),
     ?LBRKT_INDENT(State),
     enc_TransactionRequest_actions(Val#'TransactionRequest'.actions,
				    ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_TransactionRequest_actions({'TransactionRequest_actions',Val}, State) ->
    enc_TransactionRequest_actions(Val, State);
enc_TransactionRequest_actions([Mand | Opt], State) ->
    [enc_ActionRequest(Mand, State) |
     [[?COMMA_INDENT(State), enc_ActionRequest(Val, State)] || Val <- Opt]].

enc_TransactionPending(Val, State)
  when record(Val, 'TransactionPending') ->
    [?PendingToken,
     ?EQUAL,
     enc_TransactionId(Val#'TransactionPending'.transactionId, State),
     ?LBRKT_INDENT(State),
     ?RBRKT_INDENT(State)
    ].

enc_TransactionReply(Val, State)
  when record(Val, 'TransactionReply') ->
    [
     ?ReplyToken,
     ?EQUAL,
     enc_TransactionId(Val#'TransactionReply'.transactionId, State),
     ?LBRKT_INDENT(State),
     enc_immAckRequired(Val#'TransactionReply'.immAckRequired, State),
     enc_TransactionReply_transactionResult(Val#'TransactionReply'.transactionResult,
					    ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_immAckRequired(Val, State) ->
    case Val of
	asn1_NOVALUE -> [];
	'NULL'       -> [?ImmAckRequiredToken, ?COMMA_INDENT(?INC_INDENT(State))]
    end.

enc_TransactionReply_transactionResult({'TransactionReply_transactionResult',Val}, State) ->
    enc_TransactionReply_transactionResult(Val, State);
enc_TransactionReply_transactionResult({Tag, Val}, State) ->
    case Tag of
	transactionError ->
	    enc_ErrorDescriptor(Val, State);
	 actionReplies ->
	     enc_TransactionReply_transactionResult_actionReplies(Val, State)
     end.

enc_TransactionReply_transactionResult_actionReplies({'TransactionReply_transactionResult_actionReplies',Val}, State) ->
    enc_TransactionReply_transactionResult_actionReplies(Val, State);
enc_TransactionReply_transactionResult_actionReplies([Mand | Opt], State) ->
    [enc_ActionReply(Mand, State),
     [[?COMMA_INDENT(State), enc_ActionReply(Val, State)] || Val <- Opt]].

enc_ErrorDescriptor(Val, State)
  when record(Val, 'ErrorDescriptor') ->
    [
     ?ErrorToken,
     ?EQUAL,
     enc_ErrorCode(Val#'ErrorDescriptor'.errorCode, State),
     ?LBRKT,
     case Val#'ErrorDescriptor'.errorText of
	 asn1_NOVALUE ->
	     [];
	 ErrorText ->
	     enc_ErrorText(ErrorText, State)
     end,
     ?RBRKT
    ].

enc_ErrorCode({'ErrorCode',Val}, State)->
    enc_ErrorCode(Val, State);
enc_ErrorCode(Val, State) ->
    enc_DIGIT(Val, State, 0, 999).

enc_ErrorText({'ErrorText',Val}, State) ->
    enc_ErrorText(Val, State);
enc_ErrorText(Val, State)  ->
    enc_QUOTED_STRING(Val, State).

enc_ContextID({'ContextID',Val}, State) ->
    enc_ContextID(Val, State);
enc_ContextID(Val, State) ->
    case Val of
	?megaco_all_context_id    -> $*;
	?megaco_null_context_id   -> $-;
	?megaco_choose_context_id -> $$;
	Int when integer(Int) -> enc_UINT32(Int, State)
    end.

enc_ActionRequest(Val, State)
  when record(Val, 'ActionRequest') ->
    [
     ?CtxToken,
     ?EQUAL,
     enc_ContextID(Val#'ActionRequest'.contextId, State),
     ?LBRKT_INDENT(State),
     enc_list([{[Val#'ActionRequest'.contextAttrAuditReq],
		fun enc_ContextAttrAuditRequest/2}] ++
	      decompose_ContextRequest(Val#'ActionRequest'.contextRequest) ++
	      [{Val#'ActionRequest'.commandRequests,
		fun enc_CommandRequest/2}],
	      ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_ActionReply(Val, State)
  when record(Val, 'ActionReply') ->
    [
     ?CtxToken,
     ?EQUAL,
     enc_ContextID(Val#'ActionReply'.contextId, State),
     ?LBRKT_INDENT(State),
     case Val#'ActionReply'.errorDescriptor of
	 asn1_NOVALUE ->
	     enc_list(decompose_ContextRequest(Val#'ActionReply'.contextReply) ++
		      [{Val#'ActionReply'.commandReply,
			fun enc_CommandReply/2}],
		      ?INC_INDENT(State));
	 ErrorDesc when Val#'ActionReply'.contextReply == asn1_NOVALUE,
			Val#'ActionReply'.commandReply == [] ->
	     enc_ErrorDescriptor(ErrorDesc, ?INC_INDENT(State))
     end,
     ?RBRKT_INDENT(State)
    ].

decompose_ContextRequest(asn1_NOVALUE) ->
    [{[], dummy}] ;
decompose_ContextRequest(Val)
  when record(Val, 'ContextRequest') ->
    OptPriority = 
	case Val#'ContextRequest'.priority of
	    asn1_NOVALUE -> {[], dummy};
	    Prio -> {[Prio], fun enc_priority/2}
	end,
    OptEmergency = 
	case Val#'ContextRequest'.emergency of
	    asn1_NOVALUE -> {[], dummy};
	    false -> {[], dummy};
	    true -> {[?EmergencyToken], fun(Elem, _) -> Elem end}
	end,
    OptTopologyReq = 
	case Val#'ContextRequest'.topologyReq of
	    asn1_NOVALUE ->
		{[], dummy};
	    {'ContextRequest_topologyReq', asn1_NOVALUE} ->
		{[], dummy};	    
	    {'ContextRequest_topologyReq', List} ->
		{List, fun enc_TopologyRequest/2};
	    List ->
		{[List], fun enc_TopologyRequest/2}
     end,
    [OptPriority, OptEmergency, OptTopologyReq].

enc_priority(Val, State) ->
    [
     ?PriorityToken,
     ?EQUAL,
     enc_UINT16(Val, State)
    ].

enc_ContextAttrAuditRequest(Val, State)
  when record(Val, 'ContextAttrAuditRequest') ->
    [
     ?ContextAuditToken,
     ?LBRKT_INDENT(State),
     enc_list([{[Val#'ContextAttrAuditRequest'.topology],
		fun('NULL', _) -> ?TopologyToken end},
	       {[Val#'ContextAttrAuditRequest'.emergency],
		fun('NULL', _) -> ?EmergencyToken end},
	       {[Val#'ContextAttrAuditRequest'.priority],
		fun('NULL', _) -> ?PriorityToken end}],
	      ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_CommandRequest(Val, State)
  when record(Val, 'CommandRequest') ->
    [
     case Val#'CommandRequest'.optional of
	 asn1_NOVALUE ->
	     [];
	 'NULL' ->
	     "O-"
     end,
     case Val#'CommandRequest'.wildcardReturn of
	 asn1_NOVALUE ->
	     [];
	 'NULL' ->
	     "W-"
     end,
     enc_Command(Val#'CommandRequest'.command, State)
    ]. 

enc_Command({'Command',Val}, State) ->
    enc_Command(Val, State);
enc_Command({Tag, Val}, State) ->
    case Tag of
	addReq ->
	    [?AddToken, enc_AmmRequest(Val, State)];
	moveReq ->
	    [?MoveToken, enc_AmmRequest(Val, State)];
	modReq ->
	    [?ModifyToken, enc_AmmRequest(Val, State)];
	subtractReq ->
	    [?SubtractToken, enc_SubtractRequest(Val, State)];
	auditCapRequest ->
	    [?AuditCapToken, enc_AuditRequest(Val, State)];
	auditValueRequest ->
	    [?AuditValueToken, enc_AuditRequest(Val, State)];
	notifyReq ->
	    [?NotifyToken, enc_NotifyRequest(Val, State)];
	serviceChangeReq ->
	    [?ServiceChangeToken, enc_ServiceChangeRequest(Val, State)]
    end.

enc_CommandReply({'CommandReply',Val}, State) ->
    enc_CommandReply(Val, State);
enc_CommandReply({Tag, Val}, State) ->
     case Tag of
	 addReply ->
	     [?AddToken, enc_AmmsReply(Val, State)];
	 moveReply ->
	     [?MoveToken, enc_AmmsReply(Val, State)];
	 modReply ->
	     [?ModifyToken, enc_AmmsReply(Val, State)];
	 subtractReply ->
	     [?SubtractToken, enc_AmmsReply(Val, State)];
	 auditCapReply ->
	     [?AuditCapToken, enc_AuditReply(Val, State)];
	 auditValueReply ->
	     [?AuditValueToken, enc_AuditReply(Val, State)];
	 notifyReply ->
	     [?NotifyToken, enc_NotifyReply(Val, State)];
	 serviceChangeReply ->
	     [?ServiceChangeToken, enc_ServiceChangeReply(Val, State)]
     end.

enc_TopologyRequest(Val, State)
  when list(Val) ->
    [
     ?TopologyToken,
     ?LBRKT_INDENT(State),
     enc_list([{Val, fun enc_TopologyRequest1/2}],?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_TopologyRequest1(Val, State)
  when record(Val, 'TopologyRequest') ->
    [
     fun(S) ->
	     [
	      enc_TerminationID(Val#'TopologyRequest'.terminationFrom, S),
	      ?COMMA_INDENT(S), 
	      enc_TerminationID(Val#'TopologyRequest'.terminationTo, S),
	      ?COMMA_INDENT(S),
	      case Val#'TopologyRequest'.topologyDirection of
		  bothway -> ?BothwayToken;
		  isolate -> ?IsolateToken;
		  oneway ->  ?OnewayToken
	      end
	     ]
     end(?INC_INDENT(State))
    ].

enc_AmmRequest(Val, State)
  when record(Val, 'AmmRequest') ->
    [
     %% Assume that Token is added elsewhere
     ?EQUAL,
     enc_TerminationIDList1(Val#'AmmRequest'.terminationID, State),
     enc_opt_brackets(
       enc_list([{Val#'AmmRequest'.descriptors, fun enc_ammDescriptor/2}],
		?INC_INDENT(State)),
       State)
    ].

enc_ammDescriptor({Tag, Desc}, State) ->
    case Tag of
	mediaDescriptor       -> enc_MediaDescriptor(Desc, State);
        modemDescriptor       -> enc_ModemDescriptor(Desc, State);      
        muxDescriptor         -> enc_MuxDescriptor(Desc, State);   
        eventsDescriptor      -> enc_EventsDescriptor(Desc, State);      
        eventBufferDescriptor -> enc_EventBufferDescriptor(Desc, State); 
        signalsDescriptor     -> enc_SignalsDescriptor(Desc, State);    
        digitMapDescriptor    -> enc_DigitMapDescriptor(Desc, State);    
        auditDescriptor       -> enc_AuditDescriptor(Desc, State)
    end.

enc_AmmsReply(Val, State)
  when record(Val, 'AmmsReply') ->
    [
     ?EQUAL,
     enc_TerminationIDList1(Val#'AmmsReply'.terminationID, State),
     case Val#'AmmsReply'.terminationAudit of
	 asn1_NOVALUE ->
	     [];
	 [] ->
	     [];
	 TermAudit ->
	     [
	      ?LBRKT_INDENT(State) ,
	      enc_TerminationAudit(TermAudit, ?INC_INDENT(State)),
	      ?RBRKT_INDENT(State)
	     ]
     end
    ].

enc_SubtractRequest(Val, State)
  when record(Val, 'SubtractRequest') ->
    [
     %% Assume that Token is added elsewhere
     ?EQUAL,
     enc_TerminationIDList1(Val#'SubtractRequest'.terminationID, State),
     case Val#'SubtractRequest'.auditDescriptor of
	 asn1_NOVALUE ->
	     [];
	 AuditDescr ->
	     [
	      ?LBRKT_INDENT(State) ,
	      enc_AuditDescriptor(AuditDescr, ?INC_INDENT(State)),
	      ?RBRKT_INDENT(State)
	     ]
     end
    ].    

enc_AuditRequest(Val, State)
  when record(Val, 'AuditRequest') ->
    [
     %% Assume that Token is added elsewhere
     ?EQUAL,
     enc_TerminationIDList1([Val#'AuditRequest'.terminationID], State),
     case Val#'AuditRequest'.auditDescriptor of
	 asn1_NOVALUE ->
	     [];
	 AuditDescr ->
	     [
	      ?LBRKT_INDENT(State) ,
	      enc_AuditDescriptor(AuditDescr, ?INC_INDENT(State)),
	      ?RBRKT_INDENT(State)
	     ]
     end
    ].    

%% auditReply           = (AuditValueToken / AuditCapToken ) 
%% 			  ( contextTerminationAudit  / auditOther)
%% auditOther           = EQUAL TerminationID LBRKT 
%% 			  terminationAudit RBRKT
%% terminationAudit     = auditReturnParameter *(COMMA auditReturnParameter) 
%% 
%% contextTerminationAudit = EQUAL CtxToken ( terminationIDList / 
%% 			  LBRKT errorDescriptor RBRKT )
enc_AuditReply({Tag, Val}, State) ->
    case Tag of
	contextAuditResult ->
	    [
	     ?EQUAL,
	     ?CtxToken,
	     enc_TerminationIDListN(Val, State)
	    ];
	error ->
	    [
	     ?EQUAL,
	     ?CtxToken,
	     ?LBRKT_INDENT(State),
	     enc_ErrorDescriptor(Val, ?INC_INDENT(State)),
	     ?RBRKT_INDENT(State)
	    ]; 
	auditResult when record(Val, 'AuditResult') ->
	    %% auditOther
	    [
	     ?EQUAL,
	     enc_TerminationID(Val#'AuditResult'.terminationID, State),
	     ?LBRKT_INDENT(State),
	     enc_TerminationAudit(Val#'AuditResult'.terminationAuditResult, ?INC_INDENT(State)),
	     ?RBRKT_INDENT(State)
	    ]
    end.

enc_AuditDescriptor(Val, State)
  when record(Val, 'AuditDescriptor') ->
    [
     ?AuditToken,
     case Val#'AuditDescriptor'.auditToken of
	 asn1_NOVALUE ->
	     [?LBRKT, ?RBRKT];
	 List ->
	     [
	      ?LBRKT_INDENT(State),
	      enc_list([{List, fun enc_auditItem/2}], ?INC_INDENT(State)),
	      ?RBRKT_INDENT(State)
	     ]
     end
    ].

enc_auditItem(Val, _State) ->
    case Val of
	muxToken               -> ?MuxToken;
	modemToken             -> ?ModemToken;
	mediaToken             -> ?MediaToken;
	eventsToken            -> ?EventsToken;
	signalsToken           -> ?SignalsToken;
	digitMapToken          -> ?DigitMapToken;
	statsToken             -> ?StatsToken;
	observedEventsToken    -> ?ObservedEventsToken;
	packagesToken          -> ?PackagesToken;
	eventBufferToken       -> ?EventBufferToken
    end.

enc_TerminationAudit({'TerminationAudit',Val}, State) ->
    enc_TerminationAudit(Val, State);
enc_TerminationAudit([], _State) ->
    [];
enc_TerminationAudit([Mand | Opt], State) ->
  [enc_AuditReturnParameter(Mand, State),
   [[?COMMA_INDENT(State), enc_AuditReturnParameter(Val, State)] || Val <- Opt]].

enc_AuditReturnParameter({'AuditReturnParameter',Val}, State) ->
    enc_AuditReturnParameter(Val, State);
enc_AuditReturnParameter({Tag, Val}, State) ->
    case Tag of
	errorDescriptor ->
	    enc_ErrorDescriptor(Val, State);
	mediaDescriptor ->
	    enc_MediaDescriptor(Val, State);
	modemDescriptor ->
	    enc_ModemDescriptor(Val, State);
	muxDescriptor ->
	    enc_MuxDescriptor(Val, State);
	eventsDescriptor ->
	    enc_EventsDescriptor(Val, State);
	eventBufferDescriptor ->
	    enc_EventBufferDescriptor(Val, State);
	signalsDescriptor ->
	    enc_SignalsDescriptor(Val, State);
	digitMapDescriptor ->
	    enc_DigitMapDescriptor(Val, State);
	observedEventsDescriptor ->
	    enc_ObservedEventsDescriptor(Val, State);
	statisticsDescriptor ->
	    enc_StatisticsDescriptor(Val, State);
	packagesDescriptor ->
	    enc_PackagesDescriptor(Val, State);
        emptyDescriptors ->
            enc_EmptyDescriptors(Val, State)
    end.

enc_EmptyDescriptors(Val, State)
  when record(Val, 'AuditDescriptor') ->
    [
     case Val#'AuditDescriptor'.auditToken of
	 asn1_NOVALUE ->
	     [];
	 List ->
	     enc_list([{List, fun enc_auditItem/2}], ?INC_INDENT(State))
     end
    ].


enc_NotifyRequest(Val, State)
  when record(Val, 'NotifyRequest') ->
    [
     %% Assume that Token is added elsewhere
     ?EQUAL,
     enc_TerminationIDList1(Val#'NotifyRequest'.terminationID, State),
     ?LBRKT_INDENT(State),
     %% BUGBUG: Mismatch between ASN.1 and ABNF
     %% BUGBUG: The following ought to be a 'choice'
     case Val#'NotifyRequest'.errorDescriptor of
	 asn1_NOVALUE ->
	     OED = Val#'NotifyRequest'.observedEventsDescriptor,
	     enc_ObservedEventsDescriptor(OED, ?INC_INDENT(State));
	 ErrorDescr ->
	     enc_ErrorDescriptor(ErrorDescr, ?INC_INDENT(State))
     end,
     ?RBRKT_INDENT(State)
    ].

enc_NotifyReply(Val, State)
  when record(Val, 'NotifyReply') ->
    [
     %% Assume that Token is added elsewhere
     ?EQUAL,
     case Val#'NotifyReply'.terminationID of
	 asn1_NOVALUE ->
	     exit(asn1_not_compliant_with_abnf);
	 TermId ->
	     enc_TerminationIDList1(TermId, State)
     end,
     case Val#'NotifyReply'.errorDescriptor of
	 asn1_NOVALUE ->
	     [];
	 ErrorDescr ->
	     [
	      ?LBRKT_INDENT(State),
	      enc_ErrorDescriptor(ErrorDescr, ?INC_INDENT(State)),
	      ?RBRKT_INDENT(State)
	     ]
     end
    ].

enc_ObservedEventsDescriptor(Val, State)
  when record(Val, 'ObservedEventsDescriptor') ->
    [
     ?ObservedEventsToken,
     ?EQUAL,
     enc_RequestID(Val#'ObservedEventsDescriptor'.requestId, State),
     ?LBRKT_INDENT(State),
     enc_observedEventsDescriptors(Val#'ObservedEventsDescriptor'.observedEventLst, ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_observedEventsDescriptors([Mand | Opt], State) ->
    [enc_ObservedEvent(Mand, State),
     [[?COMMA_INDENT(State), enc_ObservedEvent(Val, State)] || Val <- Opt]].

%% ;time per event, because it might be buffered
%% observedEvent        = [ TimeStamp LWSP COLON] LWSP 
%% 			  pkgdName [ LBRKT observedEventParameter
%% 			  *(COMMA observedEventParameter) RBRKT ]
%% 
%% ;at-most-once eventStream, every eventParameterName at most once
%% observedEventParameter = eventStream / eventOther
enc_ObservedEvent(Val, State)
  when record(Val, 'ObservedEvent') ->
    [
     case Val#'ObservedEvent'.timeNotation of
	 asn1_NOVALUE ->
	     [];
	 TimeStamp ->
	     [
	      enc_TimeNotation(TimeStamp, State),
	      ?LWSP,
	      ?COLON
	     ]
     end,
     ?LWSP,
     enc_EventName(Val#'ObservedEvent'.eventName, State),
     enc_opt_brackets(
       enc_list([{[Val#'ObservedEvent'.streamID],   fun enc_eventStream/2},
		 {Val#'ObservedEvent'.eventParList, fun enc_eventOther/2}],
		?INC_INDENT(State)),
       State)
    ].

enc_EventName({'EventName',Val}, State) ->
    enc_EventName(Val, State);
enc_EventName(Val, State) ->
    PkgdName = ?META_ENC(event, Val),
    enc_PkgdName(PkgdName, State).

enc_eventStream(Val, State) ->
    [
     ?StreamToken,
     ?EQUAL,
     enc_StreamID(Val, State)
    ].

enc_eventOther(Val, State) 
  when record(Val, 'EventParameter') ->
    [
     enc_Name(Val#'EventParameter'.eventParameterName, State),
     enc_propertyParmValues(Val#'EventParameter'.value,
			    Val#'EventParameter'.extraInfo,
			    State)
    ].

enc_ServiceChangeRequest(Val, State)
  when record(Val, 'ServiceChangeRequest') ->
    [
     %% Assume that Token is added elsewhere
     ?EQUAL,
     enc_TerminationIDList1(Val#'ServiceChangeRequest'.terminationID, State),
     ?LBRKT_INDENT(State),
     enc_ServiceChangeParm(Val#'ServiceChangeRequest'.serviceChangeParms,
			   ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

%% serviceChangeReply   = ServiceChangeToken EQUAL TerminationID
%% 			  [LBRKT (errorDescriptor / 
%% 			  serviceChangeReplyDescriptor) RBRKT]
%% serviceChangeReplyDescriptor = ServicesToken LBRKT
%% 			  servChgReplyParm *(COMMA servChgReplyParm) RBRKT
%% 
%% ;at-most-once. Version is REQUIRED on first ServiceChange response
%% servChgReplyParm     = (serviceChangeAddress / serviceChangeMgcId /
%% 			  serviceChangeProfile / serviceChangeVersion )
enc_ServiceChangeReply(Val, State)
  when record(Val, 'ServiceChangeReply') ->
    [
     %% Assume that Token is added elsewhere
     ?EQUAL,
     enc_TerminationIDList1(Val#'ServiceChangeReply'.terminationID, State),
     enc_ServiceChangeResult(Val#'ServiceChangeReply'.serviceChangeResult, State)
     ].

enc_ServiceChangeResult({'ServiceChangeResult',Val}, State) ->
    enc_ServiceChangeResult(Val, State);
enc_ServiceChangeResult({Tag, Val}, State) ->
    case Tag of
	errorDescriptor ->
	    [
	     ?LBRKT_INDENT(State),
	     enc_ErrorDescriptor(Val, ?INC_INDENT(State)),
	     ?RBRKT_INDENT(State)
	    ];
	serviceChangeResParms ->
	     case enc_ServiceChangeResParm(Val, ?INC_INDENT(?INC_INDENT(State))) of
		 [] ->
		     [];
		 ResParms ->
		     [
		      ?LBRKT_INDENT(State),
		      ?ServicesToken,
		      fun(S) ->
			      [
			       ?LBRKT_INDENT(S),
			       ResParms,
			       ?RBRKT_INDENT(S),
			       ?RBRKT_INDENT(S)
			      ]
		      end(?INC_INDENT(State))
		     ]
	     end
     end.

%% Required length of termination ID list is 1
enc_TerminationIDList1({'TerminationIDList',Val}, State) ->
    enc_TerminationIDList1(Val, State);
enc_TerminationIDList1([Singleton], State) ->
    enc_TerminationID(Singleton, State).

%% No required length of termination ID list
enc_TerminationIDListN({'TerminationIDList',Val}, State) ->
    enc_TerminationIDListN(Val, State);
enc_TerminationIDListN(TidList, State) ->
    enc_list([{TidList, fun enc_TerminationID/2}], State).

%% TerminationID        = "ROOT" / pathNAME / "$" / "*"
%% ; Total length of pathNAME must not exceed 64 chars.
%% pathNAME             = ["*"] NAME *("/" / "*"/ ALPHA / DIGIT /"_" / "$" ) 
%% 			  ["@" pathDomainName ]
enc_TerminationID(Tid, State)
  when record(Tid,  megaco_term_id) ->
    List = [{Tid#megaco_term_id.id, fun enc_tid_component/2 }],
    enc_list(List, State, fun(_S) -> ?SLASH end, false).    

enc_tid_component(Component, State) ->
    [enc_tid_sub_component(Sub, State) || Sub <- Component].

enc_tid_sub_component(Sub, _State) ->
    case Sub of
	all    -> ?megaco_all;
	choose -> ?megaco_choose;
	Char when integer(Char) -> Char
    end.

%% mediaDescriptor      = MediaToken LBRKT mediaParm *(COMMA mediaParm) RBRKT
%% ; at-most-once per item
%% ; and either streamParm or streamDescriptor but not both
%% mediaParm            = (streamParm / streamDescriptor / 
%% 			   terminationStateDescriptor)
%% ; at-most-once
%% streamParm           = ( localDescriptor / remoteDescriptor / 
%% 			   localControlDescriptor )
%% streamDescriptor     = StreamToken EQUAL StreamID LBRKT streamParm 
%% 			  *(COMMA streamParm) RBRKT
enc_MediaDescriptor(Val, State)
  when record(Val, 'MediaDescriptor') ->
    [
     ?MediaToken,
     ?LBRKT_INDENT(State),
     enc_list([{[Val#'MediaDescriptor'.termStateDescr],
		fun enc_TerminationStateDescriptor/2} |
	       decompose_streams(Val#'MediaDescriptor'.streams)],
	      ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

decompose_streams({'MediaDescriptor_streams',Val}) ->
    decompose_streams(Val);
decompose_streams({Tag, Val}) ->
    case Tag of
	oneStream ->
	    decompose_StreamParms(Val);
	multiStream ->
	    [{Val, fun enc_StreamDescriptor/2}]
    end.

decompose_StreamParms(Val)
  when record(Val, 'StreamParms') ->
    [
     {[Val#'StreamParms'.localControlDescriptor],
      fun enc_LocalControlDescriptor/2},
     {[Val#'StreamParms'.localDescriptor],
      fun enc_localDescriptor/2},
     {[Val#'StreamParms'.remoteDescriptor],
      fun enc_remoteDescriptor/2}
    ].

enc_StreamDescriptor(Val, State) 
    when record(Val, 'StreamDescriptor') ->
    [
     ?StreamToken,
     ?EQUAL,
     enc_StreamID(Val#'StreamDescriptor'.streamID, State),
     ?LBRKT_INDENT(State),
     enc_list(decompose_StreamParms(Val#'StreamDescriptor'.streamParms),
	      ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

%% localControlDescriptor = LocalControlToken LBRKT localParm 
%% 			    *(COMMA localParm) RBRKT
%% 
%% ; at-most-once per item
%% localParm            = ( streamMode / propertyParm /
%%                          reservedValueMode  / reservedGroupMode ) 
%% reservedValueMode       = ReservedValueToken EQUAL ( "ON" / "OFF" ) 
%% reservedGroupMode       = ReservedGroupToken EQUAL ( "ON" / "OFF" ) 
%% 
%% reservedMode	     = ReservedToken EQUAL ( "ON" / "OFF" )
%% 
%% streamMode           = ModeToken EQUAL streamModes
enc_LocalControlDescriptor(Val, State)
  when record(Val, 'LocalControlDescriptor') ->
    [
     ?LocalControlToken,
     ?LBRKT_INDENT(State),
     enc_list([{[Val#'LocalControlDescriptor'.streamMode],
		fun enc_StreamMode/2},
	       {[Val#'LocalControlDescriptor'.reserveGroup],
		fun enc_reservedGroupMode/2},
	       {[Val#'LocalControlDescriptor'.reserveValue],
		fun enc_reservedValueMode/2},
	       {Val#'LocalControlDescriptor'.propertyParms,
		fun enc_PropertyParm/2}],
	      ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_reservedGroupMode(Val, _State) ->
    [
     ?ReservedGroupToken,
     ?EQUAL,
     case Val of
	 false -> ?OffToken;
	 true  -> ?OnToken
     end
    ].

enc_reservedValueMode(Val, _State) ->
    [
     ?ReservedValueToken,
     ?EQUAL,
     case Val of
	 false -> ?OffToken;
	 true  -> ?OnToken
     end
    ].

enc_StreamMode({'StreamMode',Val}, State) ->
    enc_StreamMode(Val, State);
enc_StreamMode(Val, _State) ->
    [
     ?ModeToken,
     ?EQUAL,
     case Val of
	 sendOnly -> ?SendonlyToken;
	 recvOnly -> ?RecvonlyToken;
	 sendRecv -> ?SendrecvToken;
	 inactive -> ?InactiveToken;
	 loopBack -> ?LoopbackToken
     end
    ].

enc_Name({'Name',Val}, State) ->
    enc_Name(Val, State);
enc_Name(Val, State) ->
    %% BUGBUG: NAME = ALPHA *63(ALPHA / DIGIT / "_" )
    enc_STRING(Val, State, 1, 64).

enc_PkgdName({'PkgdName', Val}, State) ->
    enc_PkgdName(Val, State);
enc_PkgdName(Val, State) ->
    %% BUGBUG:  pkgdName =  (NAME / "*")  SLASH  (ItemID / "*" )
    enc_OCTET_STRING(Val, State, 1, 64).

enc_localDescriptor(Val, State) 
  when record(Val, 'LocalRemoteDescriptor') ->
    [
     ?LocalToken,
     ?LBRKT,
     enc_LocalRemoteDescriptor(Val, State),
     ?RBRKT_INDENT(State)
    ].

enc_remoteDescriptor(Val, State) 
  when record(Val, 'LocalRemoteDescriptor') ->
    [
     ?RemoteToken,
     ?LBRKT,
     enc_LocalRemoteDescriptor(Val, State),
     ?RBRKT_INDENT(State)
    ].

%% When text encoding the protocol, the descriptors consist of session
%% descriptions as defined in SDP (RFC2327), except that the "s=", "t="
%% and "o=" lines are optional. When multiple session descriptions are
%% provided in one descriptor, the "v=" lines are required as delimiters;
%% otherwise they are optional.  Implementations shall accept session
%% descriptions that are fully conformant to RFC2327. When binary
%% encoding the protocol the descriptor consists of groups of properties
%% (tag-value pairs) as specified in Annex C.  Each such group may
%% contain the parameters of a session description.
enc_LocalRemoteDescriptor(Val, State)
  when record(Val, 'LocalRemoteDescriptor') ->
    case Val#'LocalRemoteDescriptor'.propGrps of
	[] ->
	    [];
	[OptV | MandV] ->
	    [?LfToken,
	     enc_PropertyGroup(OptV, opt_v, State) |
	     [enc_PropertyGroup(M, mand_v, State) || M <- MandV]]
    end.

enc_PropertyGroup({'PropertyGroup',Val}, RequiresV, State) ->
    enc_PropertyGroup(Val, RequiresV, State);
enc_PropertyGroup([H | _T] = List, mand_v, State) when record(H, 'PropertyParm'), H#'PropertyParm'.name == "v" ->
    enc_PropertyGroup(List, opt_v, State);
enc_PropertyGroup(PG, opt_v, State) ->
    [
     [[enc_PropertyGroupParm(PP, State), ?LfToken] || PP <- PG]
    ].

enc_PropertyGroupParm(Val, State)
  when record(Val, 'PropertyParm') ->
    [OctetString] = Val#'PropertyParm'.value,
    [
     enc_PkgdName(Val#'PropertyParm'.name, State),
     ?EqualToken,
     enc_OCTET_STRING(OctetString, State, 0, infinity)
    ].

%% propertyParm         = pkgdName parmValue
%% parmValue            = (EQUAL alternativeValue/ INEQUAL VALUE)
%% alternativeValue     = ( VALUE / LSBRKT VALUE *(COMMA VALUE) RSBRKT  / 
%% 			  LSBRKT VALUE DOT DOT VALUE RSBRKT )
enc_PropertyParm(Val, State)
  when record(Val, 'PropertyParm') ->
    PkgdName = ?META_ENC(property, Val#'PropertyParm'.name),
    [
     enc_PkgdName(PkgdName, State),
     enc_propertyParmValues(Val#'PropertyParm'.value,
			    Val#'PropertyParm'.extraInfo,
			    State)
    ].
     
enc_propertyParmValues([Single], asn1_NOVALUE, State) ->
    [
     ?EqualToken,
     enc_Value(Single, State)
    ];
enc_propertyParmValues([Single], {relation, Rel}, State) ->
    case Rel of
	greaterThan -> [$>, enc_Value(Single, State)];
	smallerThan -> [$<, enc_Value(Single, State)];
	unequalTo   -> [$#, enc_Value(Single, State)]
    end;
enc_propertyParmValues([Low, High], {range, true}, State)->
    %% Exact two values
    [
     ?EqualToken,
     ?LSBRKT,
     enc_Value(Low, State),
     ?COLON,
     enc_Value(High, State),
     ?RSBRKT
    ];
enc_propertyParmValues(Values, {sublist, true}, State)->
    %% sublist (i.e. A AND B AND ...)
    [
     ?EqualToken,
     ?LSBRKT,
     enc_list([{Values, fun enc_Value/2}], State),
     ?RSBRKT
    ];
enc_propertyParmValues(Values, {sublist, false}, State) ->
    %% alternatives (i.e. A OR B OR ...)
    [
     ?EqualToken,
     ?LBRKT,
     enc_list([{Values, fun enc_Value/2}], State),
     ?RBRKT
    ].

enc_TerminationStateDescriptor(Val, State)
  when record(Val, 'TerminationStateDescriptor') ->
    [
     ?TerminationStateToken,
     ?LBRKT_INDENT(State),
     enc_list([{Val#'TerminationStateDescriptor'.propertyParms,
		fun enc_PropertyParm/2},
	       {[Val#'TerminationStateDescriptor'.eventBufferControl],
		fun enc_eventBufferControl/2},
	       {[Val#'TerminationStateDescriptor'.serviceState],
		fun enc_serviceState/2}],
	      ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_eventBufferControl(Val, _State) ->
    [

     ?BufferToken,
     ?EQUAL,  
     case Val of
	 off      -> ?OffToken;
	 lockStep -> ?LockStepToken
    end
    ].
    
enc_serviceState({'ServiceState',Val}, State) ->
    enc_serviceState(Val, State);
enc_serviceState(Val, _State) ->
    [
     ?ServiceStatesToken,
     ?EQUAL,
     case Val of
	 test     -> ?TestToken;
	 outOfSvc -> ?OutOfSvcToken;
	 inSvc    -> ?InSvcToken
     end
    ].

enc_MuxDescriptor(Val, State)
  when record(Val, 'MuxDescriptor') ->
    [
     ?MuxToken,
     ?EQUAL,
     enc_MuxType(Val#'MuxDescriptor'.muxType, State),
     enc_TerminationIDList1(Val#'MuxDescriptor'.termList, State)
    ].

enc_MuxType({'MuxType',Val}, State) ->
    enc_MuxType(Val, State);
enc_MuxType(Val, _State) ->
    case Val of
	h221 -> ?H221Token;
	h223 -> ?H223Token;
	h226 -> ?H226Token;
	v76  -> ?V76Token
    end.

enc_StreamID({'StreamID',Val}, State) ->
    enc_StreamID(Val, State);
enc_StreamID(Val, State) ->
    enc_UINT16(Val, State).

enc_EventsDescriptor(Val, State)
  when record(Val, 'EventsDescriptor') ->
    RequestId = Val#'EventsDescriptor'.requestID,
    Events = Val#'EventsDescriptor'.eventList,
    if
	%% BUGBUG: IG 6.82 introduces parse conflict
	%% RequestId == asn1_NOVALUE, Events == [] ->
	%%     [
	%% 	?EventsToken
	%%     ];
	RequestId /= asn1_NOVALUE, Events /= [] ->
	    [
	     ?EventsToken,
	     ?EQUAL,
	     enc_RequestID(RequestId, State),
	     ?LBRKT_INDENT(State),
	     enc_list([{Events, fun enc_RequestedEvent/2}],
		      ?INC_INDENT(State)),
	     ?RBRKT_INDENT(State)
	    ]
    end.

enc_RequestedEvent(Val, State)
  when record(Val, 'RequestedEvent') ->
    PkgdName = ?META_ENC(event, Val#'RequestedEvent'.pkgdName),
    [
     enc_PkgdName(PkgdName, State),
     enc_opt_brackets(
       enc_list([{[Val#'RequestedEvent'.streamID],  fun enc_eventStream/2},
		 {Val#'RequestedEvent'.evParList, fun enc_eventOther/2} |
		 decompose_requestedActions(Val#'RequestedEvent'.eventAction)],
		?INC_INDENT(State)),
      State)
    ].

decompose_requestedActions(asn1_NOVALUE) ->
    [];
decompose_requestedActions(Val)
  when record(Val, 'RequestedActions') ->
    [
     {[Val#'RequestedActions'.keepActive],  fun enc_keepActive/2},
     {[Val#'RequestedActions'.eventDM],     fun enc_EventDM/2},
     {[Val#'RequestedActions'.secondEvent], fun enc_SecondEventsDescriptor/2},
     {[Val#'RequestedActions'.signalsDescriptor], fun enc_SignalsDescriptor/2}
    ].

enc_keepActive(Val, _State) ->
    case Val of
	true -> [?KeepActiveToken];
	false -> []
    end.
    
enc_EventDM({'EventDM',Val}, State) ->
    enc_EventDM(Val, State);
enc_EventDM({Tag, Val}, State) ->
    case Tag of
	digitMapName ->
	    [
	     ?DigitMapToken,
	     ?EQUAL,
	     enc_DigitMapName(Val, State)
	    ];
	digitMapValue ->
	    [
	     ?DigitMapToken,
	     ?LBRKT_INDENT(State),
	     enc_DigitMapValue(Val, ?INC_INDENT(State)),
	     ?RBRKT_INDENT(State)
	    ]
    end.

enc_SecondEventsDescriptor(Val, State)
  when record(Val, 'SecondEventsDescriptor') ->
    RequestId = Val#'SecondEventsDescriptor'.requestID,
    Events = Val#'SecondEventsDescriptor'.eventList,
    if
	%% BUGBUG: IG 6.82 introduces parse conflict
	%% RequestId == asn1_NOVALUE, Events == [] ->
	%%     [
	%% 	?EmbedToken
	%%     ];
	RequestId /= asn1_NOVALUE, Events /= [] ->
	    [
	     ?EmbedToken,
	     ?LBRKT_INDENT(State),
	     enc_list([{Events,	fun(V, S) -> enc_embedFirst(V, S, RequestId) end}],
		      ?INC_INDENT(State)),
	     ?RBRKT_INDENT(State)
	    ]
    end.

enc_embedFirst(Val, State, RequestId)
  when record(Val, 'SecondRequestedEvent') ->
    [
     ?EventsToken,
     ?EQUAL,
     enc_RequestID(RequestId, State),
     ?LBRKT_INDENT(State),
     %% BUGBUG: Does this really work?
     enc_list([{[Val], fun enc_SecondRequestedEvent/2}], ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_SecondRequestedEvent(Val, State)
  when record(Val, 'SecondRequestedEvent') ->
    PkgdName = ?META_ENC(event, Val#'SecondRequestedEvent'.pkgdName),
    [
     enc_PkgdName(PkgdName, State),
     enc_opt_brackets(
       enc_list(
	 [{[Val#'SecondRequestedEvent'.streamID], fun enc_eventStream/2},
	  {Val#'SecondRequestedEvent'.evParList, fun enc_eventOther/2} |
	  decompose_secondRequestedActions(Val#'SecondRequestedEvent'.eventAction)],
	 ?INC_INDENT(State)),
       State)
    ].

decompose_secondRequestedActions(asn1_NOVALUE) ->
    [];
decompose_secondRequestedActions(Val)
  when record(Val, 'SecondRequestedActions') ->
    [
     {[Val#'SecondRequestedActions'.keepActive],
      fun enc_keepActive/2},
     {[Val#'SecondRequestedActions'.eventDM],
      fun enc_EventDM/2},
     {[Val#'SecondRequestedActions'.signalsDescriptor],
      fun enc_embeddedSignalsDescriptor/2}
    ].

enc_embeddedSignalsDescriptor(Val, State) ->
    [
     ?EmbedToken,
     ?LBRKT_INDENT(State),
     enc_SignalsDescriptor(Val, ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].
    
enc_EventBufferDescriptor({'EventBufferDescriptor',Val}, State) ->
    enc_EventBufferDescriptor(Val, State);
enc_EventBufferDescriptor([Mand | Opt], State) ->
    [
     ?EventBufferToken,
     ?LBRKT_INDENT(State),
     enc_eventSpecs([Mand | Opt], ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)   
    ].

enc_eventSpecs([Mand | Opt], State) ->
    [enc_eventSpecs(Mand, State),
     [[?COMMA_INDENT(State), enc_eventSpec(Val, State)] || Val <- Opt]].

enc_eventSpec(Val, State)
  when record(Val, 'EventSpec') ->
    [
     enc_EventName(Val#'EventSpec'.eventName, State),
     enc_opt_brackets(
       enc_list([{[Val#'EventSpec'.streamID],   fun enc_eventStream/2},
		 {Val#'EventSpec'.eventParList, fun enc_eventOther/2}],
		?INC_INDENT(State)),
       State)
    ].

enc_SignalsDescriptor({'SignalsDescriptor',Val}, State) ->
    enc_SignalsDescriptor(Val, State);
enc_SignalsDescriptor(List, State) when list(List) ->
    [
     ?SignalsToken,
     ?LBRKT_INDENT(State),
     enc_list([{List, fun enc_SignalRequest/2}], ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_SignalRequest({'SignalRequest',Val}, State) ->
    enc_SignalRequest(Val, State);
enc_SignalRequest({Tag, Val}, State) ->
    case Tag of
	signal ->
	    enc_Signal(Val, State);
	seqSigList ->
	    enc_SeqSigList(Val, State)
    end.


enc_SeqSigList(Val, State)
  when record(Val, 'SeqSigList') ->
    [
     ?SignalListToken,
     ?EQUAL,
     enc_UINT16(Val#'SeqSigList'.id, State),
     ?LBRKT_INDENT(State),
     enc_list([{Val#'SeqSigList'.signalList, fun enc_Signal/2}],
	      ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_Signal(Val, State)
  when record(Val, 'Signal') ->
    [
     enc_SignalName(Val#'Signal'.signalName, State),
     enc_opt_brackets(
       enc_list([{[Val#'Signal'.streamID],         fun enc_sigStream/2},
		 {[Val#'Signal'.sigType],          fun enc_sigSignalType/2},
		 {[Val#'Signal'.duration],         fun enc_sigDuration/2},
		 {[Val#'Signal'.notifyCompletion], fun enc_notifyCompletion/2},
		 {[Val#'Signal'.keepActive],       fun enc_keepActive/2},
		 {Val#'Signal'.sigParList,         fun enc_sigOther/2}],
		?INC_INDENT(State)),
      State)
    ].

enc_sigStream(Val, State) ->
    [
     ?StreamToken,
     ?EQUAL, 
     enc_StreamID(Val, State)
    ].

enc_sigSignalType(Val, State) ->
    [
     ?SignalTypeToken,
     ?EQUAL,
     enc_SignalType(Val, State)
    ].

enc_sigDuration(Val, State) ->
    [
     ?DurationToken,
     ?EQUAL,
     enc_UINT16(Val, State)
    ].

enc_notifyCompletion(List, State) when list(List) ->
    [
     ?NotifyCompletionToken,
     ?EQUAL,
     ?LBRKT_INDENT(State),
     enc_list([{List, fun enc_notifyCompletionItem/2}], ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_notifyCompletionItem(Val, _State) ->
    case Val of
	onTimeOut                   -> ?TimeOutToken;
        onInterruptByEvent          -> ?InterruptByEventToken;
        onInterruptByNewSignalDescr -> ?InterruptByNewSignalsDescrToken;
        otherReason                 -> ?OtherReasonToken
    end.

enc_SignalType({'SignalType',Val}, State) ->
    enc_SignalType(Val, State);
enc_SignalType(Val, _State) ->
    case Val of
	brief ->   ?BriefToken;
	onOff ->   ?OnOffToken;
	timeOut -> ?TimeOutToken
    end.

enc_SignalName({'SignalName',Val}, State)->
    enc_SignalName(Val, State);
enc_SignalName(Val, State) ->
    PkgdName = ?META_ENC(signal, Val),
    enc_PkgdName(PkgdName, State).

enc_sigOther(Val, State)
  when record(Val, 'SigParameter') ->
    [
     enc_Name(Val#'SigParameter'.sigParameterName, State),
     enc_propertyParmValues(Val#'SigParameter'.value,
			    Val#'SigParameter'.extraInfo,
			    State)
    ].

enc_RequestID({'RequestID',Val}, State) ->
    enc_RequestID(Val, State);
enc_RequestID(Val, _State) when Val == ?megaco_all_request_id ->
    "*";
enc_RequestID(Val, State) ->
    enc_UINT32(Val, State).

enc_ModemDescriptor(Val, State)
  when record(Val, 'ModemDescriptor') ->
    [
     ?ModemToken,
     %% BUGBUG: Does never generate: EQUAL modemType
     ?LSBRKT,
     enc_list([{Val#'ModemDescriptor'.mtl,fun enc_ModemType/2}], State),
     ?RSBRKT,
     enc_opt_brackets(
       enc_list([{Val#'ModemDescriptor'.mpl, fun enc_PropertyParm/2}],
		?INC_INDENT(State)),
       State)
     %% BUGBUG: Is PropertyParm == NAME parmValue?
    ].

enc_ModemType({'ModemType',Val}, State)->
    enc_ModemType(Val, State);
enc_ModemType(Val, _State) ->
    %% BUGBUG: Does not handle extensionParameter
    case Val of
        v18    	  -> ?V18Token;
        v22    	  -> ?V22Token;
        v22bis 	  -> ?V22bisToken;
        v32    	  -> ?V32Token;
        v32bis 	  -> ?V32bisToken;
        v34    	  -> ?V34Token;
        v90    	  -> ?V90Token;
        v91    	  -> ?V91Token;
        synchISDN -> ?SynchISDNToken
    end.

enc_DigitMapDescriptor(Val, State)
  when record(Val, 'DigitMapDescriptor') ->
    [
     ?DigitMapToken,
     ?EQUAL,
     enc_DigitMapName(Val#'DigitMapDescriptor'.digitMapName, State),
     ?LBRKT_INDENT(State),
     enc_DigitMapValue(Val#'DigitMapDescriptor'.digitMapValue,
		       ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_DigitMapName({'DigitMapName',Val}, State) ->
    enc_DigitMapName(Val, State);
enc_DigitMapName(Val, State) ->
    enc_Name(Val, State).

enc_DigitMapValue(Val, State)
  when record(Val, 'DigitMapValue') ->
    [
     enc_timer(Val#'DigitMapValue'.startTimer, $T, State),
     enc_timer(Val#'DigitMapValue'.shortTimer, $S, State),
     enc_timer(Val#'DigitMapValue'.longTimer,  $L, State),
     %% BUGBUG: digitMapBody not handled at all
     enc_STRING(Val#'DigitMapValue'.digitMapBody, State, 0, infinity)
    ].

enc_timer(asn1_NOVALUE, _Prefix, _State) ->
    [];
enc_timer(Timer, Prefix, State) ->
    [
     Prefix,
     ?COLON,
     enc_DIGIT(Timer, State, 0, 99),
     ?COMMA_INDENT(State)
    ].

enc_ServiceChangeParm(Val, State)
  when record(Val, 'ServiceChangeParm') ->
    [
     ?ServicesToken,
     ?LBRKT_INDENT(State),
     enc_list([{[Val#'ServiceChangeParm'.serviceChangeMethod],
		fun enc_ServiceChangeMethod/2},
	       {[Val#'ServiceChangeParm'.serviceChangeAddress],
		fun enc_ServiceChangeAddress/2},
	       {[Val#'ServiceChangeParm'.serviceChangeVersion],
		fun enc_serviceChangeVersion/2},
	       {[Val#'ServiceChangeParm'.serviceChangeProfile],
		fun enc_ServiceChangeProfile/2},
	       {[{reason, Val#'ServiceChangeParm'.serviceChangeReason}],
		fun enc_serviceChangeReason/2},
	       {[Val#'ServiceChangeParm'.serviceChangeDelay],
		fun enc_serviceChangeDelay/2},
	       {[Val#'ServiceChangeParm'.serviceChangeMgcId],
		fun enc_serviceChangeMgcId/2},
	       {[Val#'ServiceChangeParm'.timeStamp],
		fun enc_TimeNotation/2}],
	      ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_ServiceChangeMethod({'ServiceChangeMethod',Val}, State) ->
    enc_ServiceChangeMethod(Val, State);
enc_ServiceChangeMethod(Val, _State) ->
    [
     ?MethodToken,
     ?EQUAL,
     case Val of
        failover      -> ?FailoverToken;
        forced        -> ?ForcedToken;
        graceful      -> ?GracefulToken;
        restart       -> ?RestartToken;
        disconnected  -> ?DisconnectedToken;
        handOff       -> ?HandOffToken
     end
     %% BUGBUG: extension
    ].

enc_ServiceChangeAddress({'ServiceChangeAddress',Val}, State) ->
    enc_ServiceChangeAddress(Val, State);
enc_ServiceChangeAddress({Tag, Val}, State) ->
    [
     ?ServiceChangeAddressToken,
     ?EQUAL,
     case Tag of
	 portNumber ->
	     enc_portNumber(Val, State);
	 ip4Address ->
	     enc_IP4Address(Val, State);
	 ip6Address ->
	     enc_IP6Address(Val, State);
	 domainName ->
	     enc_DomainName(Val, State);
	 deviceName ->
	     enc_PathName(Val, State);
	 mtpAddress ->
	     enc_mtpAddress(Val, State)
     end
    ].

enc_serviceChangeVersion(Val, State) ->
    [
     ?VersionToken,
     ?EQUAL,
     enc_version(Val, State)
    ].

enc_ServiceChangeProfile(Val, State)
  when record(Val, 'ServiceChangeProfile') ->
    ProfName = ?META_ENC(profile, Val#'ServiceChangeProfile'.profileName),
    [
     ?ProfileToken,
     ?EQUAL,
     enc_Name(ProfName, State),
     ?SLASH,
     enc_version(Val#'ServiceChangeProfile'.version, State)
    ].

enc_serviceChangeReason({reason, Val}, State) ->
    case Val of
	asn1_NOVALUE ->
	    [];
	[List] when list(List) ->
	    [
	     ?ReasonToken,
	     ?EQUAL,
	     enc_Value(List, State)
	    ]
    end.

enc_serviceChangeDelay(Val, State) ->
    [
     ?DelayToken,
     ?EQUAL,
     enc_UINT32(Val, State)
    ].

enc_serviceChangeMgcId(Val, State) ->
    [
     ?MgcIdToken,
     ?EQUAL,
     enc_MId(Val, State)
    ].

enc_portNumber(Val, State) when integer(Val), Val >= 0 ->
    enc_UINT16(Val, State).
     
enc_ServiceChangeResParm(Val, State)
  when record(Val, 'ServiceChangeResParm') ->
    enc_list([{[Val#'ServiceChangeResParm'.serviceChangeAddress],
	       fun enc_ServiceChangeAddress/2},
	      {[Val#'ServiceChangeResParm'.serviceChangeVersion],
	       fun enc_serviceChangeVersion/2},
	      {[Val#'ServiceChangeResParm'.serviceChangeProfile],
	       fun enc_ServiceChangeProfile/2},
	      {[Val#'ServiceChangeResParm'.serviceChangeMgcId],
	       fun enc_serviceChangeMgcId/2},
	      {[Val#'ServiceChangeResParm'.timeStamp],
	       fun enc_TimeNotation/2}],
	     State).

enc_PackagesDescriptor({'PackagesDescriptor',Val}, State) ->
    enc_PackagesDescriptor(Val, State);
enc_PackagesDescriptor(Val, State) ->
    [
     ?PackagesToken,
     ?LBRKT_INDENT(State),
     enc_list([{Val, fun enc_PackagesItem/2}], ?INC_INDENT(State)),  
     ?RBRKT_INDENT(State)    
    ].

enc_PackagesItem(Val, State)
  when record(Val, 'PackagesItem') ->
    PkgdName = ?META_ENC(package, Val#'PackagesItem'.packageName),
    [
     enc_Name(PkgdName, State),
     "-",
     enc_UINT16(Val#'PackagesItem'.packageVersion, State)
    ].

enc_StatisticsDescriptor({'StatisticsDescriptor',Val}, State) ->
    enc_StatisticsDescriptor(Val, State);
enc_StatisticsDescriptor(List, State) when list(List) ->
    [
     ?StatsToken,
     ?LBRKT_INDENT(State),
     enc_list([{List, fun enc_StatisticsParameter/2}], ?INC_INDENT(State)),
     ?RBRKT_INDENT(State)
    ].

enc_StatisticsParameter(Val, State)
  when record(Val, 'StatisticsParameter') ->
    PkgdName = ?META_ENC(statistics, Val#'StatisticsParameter'.statName),
    case Val#'StatisticsParameter'.statValue of
	asn1_NOVALUE ->
	    [
	     enc_PkgdName(PkgdName, State)
	    ];
	[StatVal] when list(StatVal) ->
	    [
	     enc_PkgdName(PkgdName, State),
	     ?EQUAL,
	     enc_Value(StatVal, State)
	    ]
    end.

enc_TimeNotation(Val, State)
  when record(Val, 'TimeNotation') ->
    [
     enc_STRING(Val#'TimeNotation'.date, State, 8, 8), % "yyyymmdd"
     "T",
     enc_STRING(Val#'TimeNotation'.time, State, 8, 8)  % "hhmmssss"
    ].

%% BUGBUG: Does not verify that string must contain at least one char
%% BUGBUG: This violation of the is required in order to comply with
%% BUGBUG: the dd/ce ds parameter that may possibly be empty.
enc_Value({'Value',Val}, State) ->
    enc_Value(Val, State);
enc_Value(String, _State) ->
    case quoted_string_count(String, 0, true) of
	{_, 0} ->
	    [?DQUOTE, String, ?DQUOTE];
	{false, _} ->
	    [?DQUOTE, String, ?DQUOTE];
	{true, _} ->
	    [String]
    end.
 
quoted_string_count([H | T], Count, IsSafe) ->
    case ?classify_char(H) of
	safe_char   -> quoted_string_count(T, Count + 1, IsSafe);
	rest_char   -> quoted_string_count(T, Count + 1, false);
	white_space -> quoted_string_count(T, Count + 1, false);
	_           -> exit({illegal_char, H})
    end;
quoted_string_count([], Count, IsSafe) ->
    {IsSafe, Count}.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Encode an octet string, escape } by \ if necessary 
enc_OCTET_STRING(List, State, Min, Max) ->
    do_enc_OCTET_STRING(List, State, Min, Max, 0).
    
do_enc_OCTET_STRING([H | T], State, Min, Max, Count) ->
    case H of
	$} ->
	    [$\\, H | do_enc_OCTET_STRING(T, State, Min, Max, Count + 1)];
	_ ->
	    [H | do_enc_OCTET_STRING(T, State, Min, Max, Count + 1)]
    end;
do_enc_OCTET_STRING([], _State, Min, Max, Count) ->
    verify_count(Count, Min, Max),
    [].

enc_QUOTED_STRING(String, _State) when list(String) ->
    {_IsSafe, Count} = quoted_string_count(String, 0, true),
    verify_count(Count, 1, infinity),
    [?DQUOTE, String, ?DQUOTE].

%% The internal format of hex digits is a list of octets
%% Min and Max means #hexDigits
%% Leading zeros are prepended in order to fulfill Min
enc_HEXDIG(Octets, State, Min, Max) when list(Octets) ->
    do_enc_HEXDIG(Octets, State, Min, Max, 0, []).

do_enc_HEXDIG([Octet | Rest], State, Min, Max, Count, Acc) 
  when Octet >= 0, Octet =< 255  ->
    Acc2 = [hex(Octet) | Acc],
    if
	Octet =< 15 ->
	    do_enc_HEXDIG(Rest, State, Min, Max, Count + 2, ["0" | Acc2]);
	true -> 
	    do_enc_HEXDIG(Rest, State, Min, Max, Count + 2, Acc2)
    end;
do_enc_HEXDIG([], State, Min, Max, Count, Acc)
  when integer(Min), Count < Min ->
    do_enc_HEXDIG([0], State, Min, Max, Count, Acc);
do_enc_HEXDIG([], _State, Min, Max, Count, Acc)
  when integer(Min), Count < Min ->
    verify_count(Count, Min, Max),
    lists:reverse(Acc).

enc_DIGIT(Val, State, Min, Max) ->
    enc_integer(Val, State, Min, Max).

enc_STRING(String, _State, Min, Max) when list(String) ->
    verify_count(length(String), Min, Max),
    String.

enc_UINT16(Val, State) ->
    enc_integer(Val, State, 0, 65535).

enc_UINT32(Val, State) ->
    enc_integer(Val, State, 0, 4294967295).

enc_integer(Val, _State, Min, Max) ->
    verify_count(Val, Min, Max),
    integer_to_list(Val).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Encodes a list of elements with separator tokens between
%% the elements. Optional asn1_NOVALUE values are ignored.

enc_list(List, State) ->
    enc_list(List, State, fun(S) -> ?COMMA_INDENT(S) end, false).

enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) ->
    case do_enc_list(Elems, State, ElemEncoder, SepEncoder, NeedsSep) of
	[] ->
	    enc_list(Tail, State, SepEncoder, NeedsSep);
	List ->
	    [List,
	     enc_list(Tail, State, SepEncoder, true)]
    end;
enc_list([], _State, _SepEncoder, _NeedsSep) ->
    [];
enc_list(asn1_NOVALUE, _State, _SepEncoder, _NeedsSep) ->
    [].

do_enc_list(asn1_NOVALUE, _State, _ElemEncoder, _SepEncoder, _NeedsSep) ->
    [];
do_enc_list([], _State, _ElemEncoder, _SepEncoder, _NeedsSep) ->
    [];
do_enc_list([asn1_NOVALUE | T], State, ElemEncoder, SepEncoder, NeedsSep) ->
    do_enc_list(T, State, ElemEncoder, SepEncoder, NeedsSep);
do_enc_list([H | T], State, ElemEncoder, SepEncoder, NeedsSep)
  when function(ElemEncoder), function(SepEncoder) ->
    case ElemEncoder(H, State) of
	[] ->
	    do_enc_list(T, State, ElemEncoder, SepEncoder, NeedsSep);
	List when NeedsSep == true ->
	    [SepEncoder(State),
	     List, do_enc_list(T, State, ElemEncoder, SepEncoder, true)];
	List when NeedsSep == false ->
	    [List,
	     do_enc_list(T, State, ElemEncoder, SepEncoder, true)]
    end.

%% Add brackets if list is non-empty
enc_opt_brackets([], _State) ->
    [];
enc_opt_brackets(List, State) when list(List) ->
    [?LBRKT_INDENT(State), List, ?RBRKT_INDENT(State)].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Int -> list of hex chars
hex(Int) ->
    hexi(get_lo_bits(Int, 4), []).

hexi({0, Lo}, Ack) ->
    [hex4(Lo) | Ack];
hexi({Hi, Lo} , Ack) ->
    hexi(get_lo_bits(Hi, 4), [hex4(Lo) | Ack]).

hex4(Int) when Int < 10 ->
    Int + $0;
hex4(Int) ->
    ($A - 10) + Int.

get_lo_bits(Int, Size) ->
    Lo = Int band ones_mask(Size),
    Hi = Int bsr Size,
    {Hi, Lo}.

ones_mask(Ones) ->
    (1 bsl Ones) - 1.

%% Verify that Count is within the range of Min and Max
verify_count(Count, Min, Max) ->
    if
	integer(Count) ->
	    if
		integer(Min), Count >= Min ->
		    if
			integer(Max), Count =< Max ->
			    Count;
			Max == infinity ->
			    Count;
			true ->
			    exit({count_too_large, Count, Max})
		    end;
		true ->
		    exit({count_too_small, Count, Min})
	    end;
	true ->
	    exit({count_not_an_integer, Count})
    end.





More information about the erlang-questions mailing list