We demonstrate an example of two-phase encoding using communication with an electrocardiogram. This code is based on the ASN.1 specification provided in IEEE 11073-20601-2008 and submitted drafts for an application of this type.
Two-phase encoding requires the use of multiple encoding contexts to encode both the header and payload. They must therefore be declared with the rest of the pertinent variables:
ApduType apdu; AarqApdu aarq; DataProto* pDataProto; PhdAssociationInformation phdAssocInfo; OSCTXT ctxt, ctxt2; OSOCTET* msgptr; int len; const char* filename = "message.dat";
We first populate and encode the payload; specific details will vary depending on the application:
/* Populate and encode PhdAssociationInformation */ OSCRTLMEMSET (&phdAssocInfo, 0, sizeof(phdAssocInfo)); phdAssocInfo.protocol_version.numbits = 32; phdAssocInfo.protocol_version.data[0] = 0x40; phdAssocInfo.encoding_rules.numbits = 16; rtxSetBit (phdAssocInfo.encoding_rules.data, 16, EncodingRules_mder); phdAssocInfo.nomenclature_version.numbits = 32; rtxSetBit (phdAssocInfo.nomenclature_version.data, 32, NomenclatureVersion_nom_version1); phdAssocInfo.functional_units.numbits = 32; phdAssocInfo.system_type.numbits = 32; rtxSetBit (phdAssocInfo.system_type.data, 32, SystemType_sys_type_agent); { static const OSOCTET sysId[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38 } ; phdAssocInfo.system_id.numocts = (OSUINT32) 8; phdAssocInfo.system_id.data = (OSOCTET*) sysId; } phdAssocInfo.dev_config_id = extended_config_start; phdAssocInfo.data_req_mode_capab.data_req_mode_flags.numbits = 16; phdAssocInfo.data_req_mode_capab.data_req_init_agent_count = 1; phdAssocInfo.data_req_mode_capab.data_req_init_manager_count = 0; /* Create memory output stream */ stat = rtxStreamMemoryCreateWriter (&ctxt, 0, 0); if (stat < 0) { printf ("Create memory output stream failed\n"); rtxErrPrint (&ctxt); rtFreeContext (&ctxt); return stat; } /* Encode */ stat = MDEREnc_PhdAssociationInformation (&ctxt, &phdAssocInfo); msgptr = rtxStreamMemoryGetBuffer (&ctxt, &len);
In brief, the data structures used for the payload are initialized
to zero using the OSCRTLMEMSET
macro, which here acts just like
the C runtime library memset
function. The data used for
populating this example are taken from a draft specification.
After filling in the necessary fields, the rtxStreamMemoryCreateWriter
function is used to create a memory stream for encoding the payload. More
information on this function can be found in the C/C++
Common Run Time Library manual. In case of failure, errors are
trapped and reported.
Finally, the proper MDEREnc
function is called to encode
the data. A pointer to the message content is retrieved using the
rtxStreamMemoryGetBuffer
function. This pointer is used later
to fill in the contents of the payload.
After encoding the payload, the rest of the message content must be populated and encoded:
/* Initialize 2nd context structure */ stat = rtInitContext (&ctxt2); /* Populate apdu with test data */ OSCRTLMEMSET (&aarq, 0, sizeof(AarqApdu)); aarq.assoc_version.numbits = 32; rtxSetBit (aarq.assoc_version.data, 32, AssociationVersion_assoc_version1); pDataProto = rtxMemAllocType (&ctxt2, DataProto); pDataProto->data_proto_id = data_proto_id_20601; pDataProto->data_proto_info.numocts = len; pDataProto->data_proto_info.data = msgptr; rtxDListAppend (&ctxt2, &aarq.data_proto_list, pDataProto);
The msgptr
variable is used here to fill in the contents
of the data_proto_info
structure. The rest of the contents
are initialized and then encoded:
apdu.t = T_ApduType_aarq; apdu.u.aarq = &aarq; /* Create memory output stream */ stat = rtxStreamMemoryCreateWriter (&ctxt2, 0, 0); if (stat < 0) { printf ("Create memory output stream failed\n"); rtxErrPrint (&ctxt); rtFreeContext (&ctxt); } /* Encode */ stat = MDEREnc_ApduType (&ctxt2, &apdu);
Again, a memory stream writer is used here for encoding, but other options exist to write to a file or a socket.