UM C API  6.16
C PDM Details

The LBM Pre-Defined Message(PDM) API provides a framework for applications to create message definitions and messages from those definitions. A PDM definition contains a list of field information describing the fields that will be contained in a message. A PDM message contains one or more fields with each field corresponding to a specific field information object in the definition. Field info consists of:

  • A name (string names or integer names are supported).
  • A type (discussed below).
  • If the field is required. Message fields consist of:
  • A handle to the corresponding field info.
  • A value (particular to the field type). Each named field may only appear once in a message. If multiple fields of the same name and type are needed, an array field can be used to store multiple values for that field. PDM messages can be added as a field to other PDM messages by using the field type PDM_MESSAGE.
Field types
The following field types (and arrays thereof) are supported by PDM:
Description PDM Type C Type
Boolean PDM_TYPE_BOOLEAN uint8_t
8-bit signed integer PDM_TYPE_INT8 int8_t
8-bit unsigned integer PDM_TYPE_UINT8 uint8_t
16-bit signed integer PDM_TYPE_INT16 int16_t
16-bit unsigned integer PDM_TYPE_UINT16 uint16_t
32-bit signed integer PDM_TYPE_INT32 int32_t
32-bit unsigned integer PDM_TYPE_UINT32 uint32_t
64-bit signed integer PDM_TYPE_INT64 int64_t
64-bit unsigned integer PDM_TYPE_UINT64 uint64_t
Single-precision floating point PDM_TYPE_FLOAT float
Double-precision floating point PDM_TYPE_DOUBLE double
Decimal PDM_TYPE_DECIMAL struct decimal
Timestamp PDM_TYPE_TIMESTAMP struct timestamp
Fixed Length String PDM_TYPE_FIX_STRING char *
String PDM_TYPE_STRING char *
Fixed Length Unicode PDM_TYPE_FIX_UNICODE char *
Unicode PDM_TYPE_UNICODE char *
Nested PDM message PDM_TYPE_MESSAGE lbmpdm_msg_t *
Binary large object (BLOB) PDM_TYPE_BLOB void *
Note that arrays are homogeneous.
Creating a message definition
A message definition must be defined (via lbmpdm_defn_create()) before a message can be created. After creating the definition, field info can be added. Once all field information has been added to a definition, the definition must be finalized (lbmpdm_defn_finalize()). Each definition must be given an id to identify that definition (and messages created with the definition) to all consumers of the message. This allows all messaging participants to define the message and for PDM to quickly deserialize the messages. As an example, we will create a simple definition with two fields, a 32-bit signed integer and a string array. We will give the definition an id of 1000.
printf("Failed to create the definition");
return;
}
info_attr.flags = 0;
info_attr.flags |= PDM_FIELD_INFO_FLAG_REQ;
info_attr.req = PDM_FALSE;
The values of the info_attr struct will only be examined if the corresponding PDM_FIELD_INFO_FLAG_* has been set when it is passed to the add_field_info function. If NULL is passed instead of an info_attr pointer or any of the flags are not set, then default values will be used when adding the field info(which are: required = PDM_TRUE, fixed string length = 0, and number of array elements = 0).
Once the definition exists, a message can be created and field values can be set in the message. The fields can be set by using the field handle that was returned from the call to the definition to add field info (or the field handle can be looked up from the definition).
Sample code
Checking of return codes has been omitted but should be done during actual usage of pdm.
{
int rc;
char *msg_buffer;
int msg_len;
int32_t quant = 50;
char *str_arr[3] = {"s1", "str2", "string3"};
size_t str_len_arr[3] = {3, 5, 8};
size_t str_arr_num_elem = 3;
int32_t rcv_quant;
size_t rcv_quant_sz = sizeof(int32_t);
char *rcv_str_arr[3];
rcv_str_arr[0] = malloc(3);
rcv_str_arr[1] = malloc(5);
rcv_str_arr[2] = malloc(8);
lbmpdm_msg_create(&msg1, defn, 0);
rc = lbmpdm_msg_set_field_value(msg1, h1, &quant, 0);
rc = lbmpdm_msg_set_field_value_vec(msg1, h2, str_arr, str_len_arr, str_arr_num_elem);
msg_len = lbmpdm_msg_get_length(msg1);
msg_buffer = malloc(msg_len);
rc = lbmpdm_msg_serialize(msg1, msg_buffer);
rc = lbmpdm_msg_create(&msg2, defn, 0);
rc = lbmpdm_msg_deserialize(msg2, msg_buffer, msg_len);
rc = lbmpdm_msg_get_field_value(msg2, h1, &rcv_quant, &rcv_quant_sz);
rc = lbmpdm_msg_get_field_value_vec(msg2, h2, rcv_str_arr, str_len_arr, &str_arr_num_elem);
printf("rcv_quant = %d\n", rcv_quant);
printf("rcv_str_arr[0] = %s\n", rcv_str_arr[0]);
printf("rcv_str_arr[1] = %s\n", rcv_str_arr[1]);
printf("rcv_str_arr[2] = %s\n", rcv_str_arr[2]);
free(rcv_str_arr[0]);
free(rcv_str_arr[1]);
free(rcv_str_arr[2]);
free(msg_buffer);
}
Creating a message
Messages are created from definitions. The line above that is used to create the message:
lbmpdm_msg_create(&msg1, defn, 0);
Setting field values in a message
Scalar (non-array) fields are added to a message via the lbmpdm_msg_set_field_value() to set a field's value using its handle.
When setting a field's value, data of the appropriate type must be supplied. A 0 length can be supplied for fixed length fields but it is recommended to pass the correct size in bytes to ensure the correct number of bytes are copied into the message. As an example, to set a 32-bit signed integer field to a message:
rc = lbmpdm_msg_set_field_value(msg1, h1, &quant, 0);
Setting a string value is done by passing the char * and the actual size of the string in bytes (normally this is the num_chars + 1 to account for the null character but for UNICODE types this value will be larger). For the MESSAGE type, the value argument should be a lbmpdm_msg_t * and the length argument should be sizeof(lbmpdm_msg_t *) because the setter will attempt to access the lbmpdm_msg_t via the pointer and then serialize it to bytes via its serialize method.
Setting the value of array fields in a message
To set an array's value in a message, call the lbmpdm_msg_set_field_value_vec() API function.
As an example, the code that sets the string array field value is:
rc = lbmpdm_msg_set_field_value_vec(msg1, h2, str_arr, str_len_arr, str_arr_num_elem);
Setting an array field value requires an addition number of elements parameter and expects an array of lengths (one for each element in the array). Again, string types should pass a length array that indicates the size in bytes of each string rather than the number of characters as shown in the example code which uses sizes a length array of {3, 5, 8}.
Serializing the message
Once a PDM message is constructed, it must be serialized for transmission. The API function lbmpdm_msg_serialize() serializes the message to the buffer provided. The length of the serialized data may be obtained via the API function lbmpdm_msg_get_length(). For example, a constructed message may be sent by:
rc = lbmpdm_msg_serialize(msg1, msg_buffer);
rc = lbm_src_send(src, msg_buffer, msg_len, 0);
Deserializing a message
When the bytes for a pdm message are received, they must be deserialized so that individual fields can be accessed. The lbmpdm_msg_t should be reused when possible to deserialize multiple incoming messages. This is done via the lbmpdm_msg_deserialize() API function:
rc = lbmpdm_msg_deserialize(msg2, msg_buffer, msg_len);
// Deserializing an lbm message would be the following
//rc = lbmpdm_msg_deserialize(msg2, lbmmsg->data, lbmmsg->len);
Fetching fields from a message
When fetching a field from a message, the field should be accessed by its handle.
Scalar (non-array) fields may be retrieved via the lbmpdm_msg_get_field_value().
rc = lbmpdm_msg_get_field_value(msg2, h1, &rcv_quant, &rcv_quant_sz);
Array fields may be retrieved via the lbmpdm_msg_get_field_value_vec().
rc = lbmpdm_msg_get_field_value_vec(msg2, h2, rcv_str_arr, str_len_arr, &str_arr_num_elem);
When accessing a field, it is expected that value pointer being provided already points to an area of sufficient size to hold the field's value. The len argument should indicate the size or sizes (for array types) of the available space. If the size is not sufficient, a PDM_FAILURE will be returned and the failing len argument will be updated to indicate the actual size needed. For array types, the same logic applies to the number of elements argument, where if the number of allocated elements indicated by the input parameter is insufficient, the call will fail and the value will be updated to indicate the needed number of elements. For the MESSAGE type, the value argument should be a lbmpdm_msg_t * that points to an empty lbmpdm_msg_t which has been created with a NULL definition. The length argument should be sizeof(lbmpdm_msg_t *) because the setter will attempt to access the lbmpdm_msg_t via the pointer and then deserialize the bytes into it.
Disposing of a message and definition
Once a PDM message (created by either the lbmpdm_msg_create() or lbmpdm_msg_deserialize() API calls) is no longer needed, it must be deleted to avoid a resource leak. This is done via the lbmpdm_msg_delete() API call.
Error information
All functions return a value to indicate the success of failure of the operation. Most return PDM_SUCCESS to indicate success, or PDM_FAILURE otherwise. Consult the individual function documentation for exceptions.
The function lbmpdm_errmsg() will return a descriptive error message.
Additional Information
When adding arrays to a definition, an array length can be specified in the info attributes. A 0 length means that the array's length is variable and will be determined when the array is set in the actual message. A positive number for the array length will create a fixed array of that size.
When adding PDM_TYPE_FIX_STRING, PDM_TYPE_FIX_STRING_ARR, PDM_TYPE_FIX_UNICODE, or PDM_TYPE_FIX_UNICODE_ARR, field information to a definition, a fixed string length must be specified in the info attributes. The value should be the number of characters in the string (excluding the null character). The appropriate amount of space will be then allocated in the message for each of the expected fixed strings (for the FIX_STRING types, there will be num_chars + 1 bytes allocated per string and for the FIX_UNICODE types, there will be 4 * num_chars + 1 bytes allocated per string). By using fixed strings for a field, as well as making the field required (and specifying a fixed size for the array types), the best performance and size can be achieved because the field will be optimized as a "fixed required" field.
When setting and getting field values of type FIX_STRING and FIX_UNICODE (and the corresponding arrays), extra care should be made to ensure the len parameters are correct. When setting the value, the len should indicate the actual number of bytes represented by the string that should be copied (which should include the null character). If this is less than the size indicated to the definition when setting up the field information, the rest of the space will be zeroed out. When getting the value, enough space should be allocated for the entire size of the fixed string field, which as described above should be the number of characters + 1 for the string types and 4 * the number of characters + 1 for the unicode types.
An additional way to get a field value from a message is by using the lbmpdm_msg_get_field_value_stct method, which does not require the storage and lengths to be allocated beforehand but instead will allocate everything during the call and set all of the appropriate values of the provided lbmpdm_field_value_t. Although simpler to use, the drawback is not being able to use preallocated space to hold the field value as the other get_field_value methods are able to do. It also requires the application to manage the field_value_t and call the field_value_stct_delete method when finished to clean up the allocated memory inside the field_value_t.