GET: Return a decimal64 Value to the Client


The most common usage of the decimal64 data type is for operational data or RPC operation output data. In all of these cases, it should be possible to use (or obtain) the obj_template_t * structure pointer for the decimal64 leaf or leaf-list object.


    container top-state {
      config false;
      leaf dec2 {
        type decimal64 {
          fraction-digits 3;
        }
      }
    }


The GET2 callback code generated for "top-state" container (tdec_top_state_get) contains this "for loop" at the end of the function:


    /* go through all the requested terminal child objects */
    obj_template_t *childobj =
        getcb_first_requested_child(get2cb, obj);
    for (; childobj; childobj =
        getcb_next_requested_child(get2cb, childobj)) {

        const xmlChar *name = obj_get_name(childobj);

        /* Retrieve the value of this terminal node and
         * add with getcb_add_return_val */

        if (!xml_strcmp(name, y_tdec_N_dec2)) {
            /* leaf dec2 (decimal64) */

        }
    }


The code for the "dec2" leaf will be modified:


Example Stub code:

        if (!xml_strcmp(name, y_tdec_N_dec2)) {
            /* leaf dec2 (decimal64) */
        }


In this example, there will be a static function to retrieve the "dec2" value from the system.

A val_value_t structure is created and added to the "getcb" control block:


Example Filled Code Stub:

        if (!xml_strcmp(name, y_tdec_N_dec2)) {
            /* leaf dec2 (decimal64) */
            val_value_t *childval = get_dec2_value(childobj, &res);
            if (childval) {
                getcb_add_return_val(get2cb, childval);
            } else {
                return res;
            }
        }


The example system GET callback "get_dec2_value":

/********************************************************************
* FUNCTION get_dect2_value
*
* Example system access GET callback that creates a val_value_t
* return value
*
* INPUTS:
*     obj = object template to use
* OUTPUS:
*     *res == return status (NO_ERR if return non-NULL)
* RETURNS:
*     malloced val_value_t containing the dec2 value from the system
********************************************************************/
static val_value_t *get_dec2_value (obj_template_t *obj,
                                    status_t *res)
{
    *res = NO_ERR;

    /* replace the following line with some function that
     * gets a string representation of the decimal64
     * in this case, 3 fraction digits are expected
     */
    const xmlChar *valstr = (const xmlChar *)"12.003";

    /* make a simple value using the string */
    val_value_t *retval =
        val_make_simval_obj(obj, valstr, res);
    return retval;
}


If the client requested the "top-state" container then the following data would be returned:


Example GET Response:

<rpc-reply message-id="2"
 xmlns:ncx="http://netconfcentral.org/ns/yuma-ncx"
 ncx:last-modified="2020-11-03T20:19:35Z" ncx:etag="7506"
 xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
 <data>
  <top-state xmlns="http://netconfcentral.org/ns/tdec">
   <dec2>12.003</dec2>
  </top-state>
 </data>
</rpc-reply>



SET: Use a decimal64 RPC input value


If a decimal64 type is used in configuration data or RPC input parameters, then the SIL or SIL-SA code will be presented with the input parameters as a queue of val_value_t structure.


Example YANG RPC operation:

    rpc dec-rpc {
      input {
        leaf dec3 {
          type decimal64 {
            fraction-digits 4;
          }
        }
      }
      output {
        leaf dec4 {
          type decimal64 {
            fraction-digits 4;
          }
        }
      }
    }


Assume the client invokes the "dec-rpc" operation and sets to "dec4" input parameter to the value "0.005"


Example RPC Request:

<rpc message-id="1"
 xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
 <dec-rpc xmlns="http://netconfcentral.org/ns/tdec">
  <dec3>0.0005</dec3>
 </dec-rpc>
</rpc>


The SIL code that is generated for the "dec-rpc" invoke callback contains some auto-generated code to retrieve the "dec4" input parameter:


Example SIL Code Stub:

    val_value_t *inputval = agt_get_rpc_input(msg);
    if (inputval == NULL) return ERR_NCX_OPERATION_FAILED;

    val_value_t *v_dec3_val = NULL;
    int64 v_dec3;

    v_dec3_val =
        val_find_child(inputval,
                       y_tdec_M_tdec,
                       y_tdec_N_dec3);
    if (v_dec3_val) {
        v_dec3 = VAL_DEC64(v_dec3_val);
    }


The auto-generated code is not that useful because the "v_dec3" value is just part of the internal representation of a decimal64 value.  In order to convert the value to its string representation, the "val_make_sprintf_string" function can be used:


Example SIL Code Filled:

    val_value_t *v_dec3_val =
        val_find_child(inputval,
                       y_tdec_M_tdec,
                       y_tdec_N_dec3);
    if (v_dec3_val) {
        xmlChar *buff = val_make_sprintf_string(v_dec3_val);
        if (buff) {
            /* do something with the value in 'buff' */
            log_debug("\nGot dec3 value '%s'", buff);

            /* must free the buffer after use */
            m__free(buff);
        }
    }


The internal server log traces for the RPC operation will include this log message if the --log-level is set to "debug" or higher.


Example server log output:

Start SIL validate rpc <dec-rpc> from module tdec
Start SIL invoke rpc <dec-rpc> from module tdec
Got dec3 value '0.0005'
agt_rpc: Sending <ok> for empty data reply
agt_rpc: sending ok <rpc-reply> for ses 3 msg '2'



Internal Representation: ncx_dec64_t


The file ncx/ncx_num.c contains access functions for ncx_num_t.


The internal data type ncx_num_t is used to store numeric types such as decimal64.

The ncx_dec64_t structure within the ncx_num_t stores a decimal64 value in 3 fields.


typedef struct ncx_dec64_t_ {
    int64  val;         /* adjusted number to fit in 64 bits */
    uint8  digits;      /* number of decimal digits 1 .. 18 */
    uint8  zeroes;      /* number of leading zeroes 0 .. 17 */
} YPACK ncx_dec64_t;


Macros to Access decimal64 fields


#define VAL_DEC64(V)          (V)->v.num.dec.val
#define VAL_DEC64_DIGITS(V)   (V)->v.num.dec.digits
#define VAL_DEC64_ZEROES(V)   (V)->v.num.dec.zeroes


ncx_decode_dec64


SIL code may need to use an API that represents a number using ncx_num_t.

If so then the ncx_decode_dec64 function can be used to convert a string to ncx_num_t structure.


/********************************************************************
* FUNCTION ncx_decode_dec64
*
* Handle some sort of decimal64 number string (NCX_BT_DECIMAL64)
*
* INPUTS:
*   numstr == number string
*    digits == number of expected digits for this decimal64
*   retnum == pointer to initialized ncx_num_t to hold result
*
* OUTPUTS:
*   *retnum == converted number
*
* RETURNS:
*   status of the operation
*********************************************************************/
extern status_t
    ncx_decode_dec64 (const xmlChar *numstr,
                      uint8  digits,
                      ncx_num_t  *retnum);


Example: Convert the string "4.05" to an ncx_num_t.

      leaf dec1 {
        type decimal64 {
          fraction-digits 2;
        }
      }


  • numstr is the input set to string "4.05"
  • decnumis the output
    • Must be initialized before use with ncx_init_num
    • Fields of decnum are set by ncx_decode_dec64
    • Must be cleaned with ncx_clean_num after use

 

Example Use of ncx_decode_dec64

            const xmlChar *numstr = "4.05";
            ncx_num_t decnum;
            ncx_init_num(&decnum);
            typ_def_t *typdef = VAL_TYPDEF(val);
            res =
                ncx_decode_dec64(numstr,
                                 typ_get_fraction_digits(typdef),
                                 &decnum);
            if (res == NO_ERR) {
                // do something with decnum
            }

            ncx_clean_num(&decnum);