An ASN.1 Format Example

This appendix shows how incoming ASN.1 records can be encoded into a sequential external format.

An ASN.1 format definition can be pasted directly into an Ultra asn_block. Nested structures are saved as they are in the target_internal type, however the nested fields cannot easily be encoded to a different format without using an APL agent (Analysis or Aggregation). In the example, the incoming ASN.1 record is encoded to a sequential record, mapping all the field values to corresponding fields in the sequential format.

Mapping - nested structures to plain, sequential structures - can be accomplished in three ways:

  • By creating a constructed external format definition, that is, where the external record definition consists of sub-externals.

  • By extending the target_internal with first level temporary fields which hold the values of the nested fields to be encoded.

  • By creating a new UDR which is populated with values from the incoming nested UDR.

Constructed Internal

It is possible to encode to a constructed sequential external, that is, an external sequential definition containing other externals to represent the nested fields. The disadvantage with this approach is that it is not possible to mix different level fields in the produced output record.

external

Name the fields in the outgoing external exactly as in the incoming ASN.1 structure. This allows the use of automatic mapping.

asn_block {

exchangeRec DEFINITIONS IMPLICIT TAGS ::= 
BEGIN

main_udr   ::= SEQUENCE 
{ 
  duration        [ APPLICATION 1 ]    INTEGER OPTIONAL, 
  calledNumber    [ APPLICATION 2 ]    INTEGER OPTIONAL, 
  callingNumber subUDR1 
}

    subUDR1 ::= [ APPLICATION 3 ] SEQUENCE 
    { 
       category    [ APPLICATION 4 ]    INTEGER OPTIONAL, 
       adressString subUDR2 
    }

        subUDR2 ::= [ APPLICATION 5 ] SEQUENCE 
        { 
           number [ APPLICATION 6 ] INTEGER OPTIONAL, 
           ton [ APPLICATION 7 ] INTEGER OPTIONAL, 
           npi [ APPLICATION 8 ] INTEGER OPTIONAL 
        }

END 
};

//-------------------------------------------------------

external out { 
  ascii duration : static_size(2); 
  ascii calledNumber : static_size(8); 
  subUDR1 callingNumber : static_size(8); 
};

external subUDR1 { 
  ascii category : static_size(2); 
  subUDR2 adressString : static_size(6); 
};

external subUDR2 { 
  ascii number : static_size(2); 
  ascii ton : static_size(2); 
  ascii npi : static_size(2); 
}; 

in-map and out_map

Automatic mapping considers sub-UDRs as well.

in_map inM : external( main_udr ),
               target_internal( myTI ) {
  automatic;
}; 

out_map outM : internal( myTI ),
               external( out ) {
                 automatic;
}; 

decoder and encoder

decoder myDec : in_map( inM );

encoder myEnc : out_map( outM );

Extending the target_internal

Create an  internal which holds the nested fields to be mapped to the sequential format. Define a target_internal holding both the asn_block and the internal. The values of the nested fields of the target_internal are copied to the fields added with the internal format specification, using APL code.

APL code is necessary to extract the nested values

Format Definition (shortened):


asn_block {
:
  main_udr
:
}; 



Note!

All following fields are declared optional. This is to enable differentiation between an absent value and a zero value (default for int type). Thus, if no value is entered it is encoded as empty in output format, as opposed to 0 (zero).

internal exchangeRec_Int {
  int duration_i         : optional;
  string calledNumber_i  : optional;
  int category_i         : optional;
  string number_i        : optional;
  string ton_i           : optional;
  string npi_i           : optional;
};

Note!

Since no automatic mapping specifications are given, no named internal types for SubUDR1 and SubUDR2 are received. This is not a problem as long as referencing the types directly (for instance in APL) is unnecessary.

in_map exchangeRecord_MAP_IN:
  external(MainUdr),
  internal(exchangeRec_Int),
  target_internal(exchangeRec_TI){
    e:duration      and  i:duration_i;
    e:calledNumber  and  i:calledNumber_i;
    automatic;
}; 

A structure of sub-UDRs with the same field names as in the internal mapped from is created (that is, the target_internal which has the same structure as the ASN.1 external). This only produces a line-based comma separated output file.

external ConstructedOut: terminated_by("\n") {
  ascii duration : terminated_by(","), int(base10);
  ascii calledNumber : terminated_by(",");
  SubOut1 callingNumber;
};

external SubOut1: terminated_by("\n") {
  ascii category : terminated_by(","), int(base10);
  SubOut2 adressString;
};

external SubOut2: terminated_by("\n") {
  ascii number : terminated_by(",");
  ascii ton : terminated_by(",");
  ascii npi : terminated_by("\n");
};

out_map Constructed_Map:
   external(ConstructedOut),
   internal(exchangeRec_TI) {
    e:duration      and  i:duration_i;
    e:calledNumber  and  i:calledNumber_i;
    automatic;
};


encoder ConstructedEnc: out_map(Constructed_Map);


APL Code:

consume {

  if ( udrIsPresent( input.callingNumber.adressString ) ) {
     input.category_i = input.callingNumber.category;
     input.number_i = input.callingNumber.adressString.number;
     input.ton_i = input.callingNumber.adressString.ton;
     input.npi_i = input.callingNumber.adressString.npi;
  }

  udrRoute( input );
} 


Creating a New UDR

Create an internal for the sequential format, however do not add it to the incoming ASN.1 structure's target_internal. Instead for each incoming UDR, create a new UDR and copy the field values from the ASN.1 UDR. The ASN.1 UDR can then be discarded, routing the new internal further.


Internal presentation of the input ASN.1 UDR


Internal presentation of the output sequential UDR

APL Code Definition:

exRec.exchangeRec_Int outUDR = udrCreate( exRec.exchangeRec_Int );

consume {
  outUDR.duration_i = input.duration;
  outUDR.calledNumber_i = input.calledNumber;
    
  if ( udrIsPresent( input.callingNumber.adressString ) ) {
     outUDR.category_i = input.callingNumber.category;
     outUDR.number_i = input.callingNumber.adressString.number;
     outUDR.ton_i = input.callingNumber.adressString.ton;
     outUDR.npi_i = input.callingNumber.adressString.npi;
  }

  udrRoute( outUDR );
} 

external

The ASN.1 definition can be copied directly into an Ultra asn_block definition. An external for the sequential outgoing UDRs is created.

Format Definition (shortened):

asn_block { 
: 
main_udr 
: 
};
 
// The same field names as in the internal format are used
// to be able to use automatic mapping.
external exchangeRecSEQ 
  ascii duration_i 
  ascii calledNumber_i 
  ascii category_i 
  ascii number_i 
  ascii ton_i 
  ascii npi_i 
};


internal

An internal, containing fields matching the external field names, is created in order to hold the values to be encoded.

internal exchangeRec_Int {
  int duration_i         : optional;
  string calledNumber_i  : optional;
  int category_i         : optional;
  string number_i        : optional;
  string ton_i           : optional;
  string npi_i           : optional;
};  

in_map and out_map

The incoming  external  is turned into a  target_internal , without adding any fields. The  internal  is mapped to the sequential external format.

in_map exchangeRecord_MAP_IN_II:
    external(main_udr),
    target_internal(exRec_TI){
      automatic;
};

out_map exchangeRecord_MAP_OUT:
   external(exchangeRecSEQ),
   internal(exchangeRec_Int) {
    automatic;
};

decoder and encoder

decoder exchangeRec: in_map(exchangeRecord_MAP_IN);

encoder exchangeRecSEQ: out_map(exchangeRecord_MAP_OUT);