A DMH Client Library for Multi-Threaded C Applications


This is the User Guide and Reference document for a multi-threaded C client library for the Hume Distributed Message Hub (DMH) message system.   The library provides high-level methods for sending and receiving binary or text messages, with either send-and-reply synchronous-style interactions, or higher performance asynchronous-style interactions.  POSIX threads are used to implement buffered sending, and well as asynchronous receiving, so there is not a need for the application to support an event loop, or a need to integrate the library code into an event loop.  Although the C library can be used by C++ applications, Hume Integration Software offers a high-level C++ DMH Client library, DmhCppClient, which features the strong advantage of automatic memory management as well as lesser advantages such as default argument values.

The DMH Client library has also been ported to Windows using an open source POSIX threads library, PTHREADS-WIN32. The PTHREADS-WIN32 library is distributed per the terms of the GNU Library License, and source code is available at http://sourceware.org/pthreads-win32/ and through Hume Integration.

Message exchanges are typically directed to application servers such as the Hume Integration Datahub, or the dmh_SQLsrv persistent database interface.  The DMH message system is remarkably easy to use with these DMH Tcl processes because the messages are typically SQL, Tcl, or VFEI text that is directly interpreted by the receiver.   To extend this ease of use to the C client, functions are provided to parse Tcl list text strings,  or to create Tcl list text strings from string elements.  The library can also be used to exchange binary messages which can be useful for equipment controller and device integration.

Feature Summary


User Guide


The DMH client library for multi-threaded C applications is provided as the following files:

The files are distributed in a .zip archive named  dmh_mt_xxx.zip. where xxx indicates the platform or compiler version.  If you received the DMH software on CDROM, this archive is in the subdirectory LAN_IMAGE.  The runtime and development files are also provided in releases of the Hume SECS C++ library.  There are separate SECS distributions for 32bit and 64bit versions.

The library is a client library that connects to a DMH message system server such as Hume's  Tcl/Tk Datahub application, or the Java Datahub.   The library is is designed to successfully interoperate with versions of the DMH software running on a wide variety of platforms which provides the ability to easily bridge computer systems, application processes, and programming languages.

Installation

Installation consists of extracting files in the dmh_mt_xxx.zip archive to a directory or directories of your choice. If you are installing the software for shared usage, you may want to put dmh.h in the /usr/local/include directory, and put the libdmh.a file in the /usr/local/lib directory. 

Development

Your design will typically use one instance of a DMH client connection per process.  A single client connection is used for sending and receiving with multiple mailboxes simultaneously from multiple threads.  More than one instance of a client connection per process is only needed if you wish to communicate directly with multiple DMH servers.  You should not use multiple connection instances within a single process to communicate with a single DMH server.

You will usually have the Hume Datahub SDK software installed on your development system.   One reason for this requirement is to insure that you have the online documentation for the DMH system.  A second major reason is so that you can run a DMH server process on your development system to test and debug against.

If you are not familiar with the DMH message system, you may want to read the mbx document which is usually installed at /usr/local/htm85/mann/mbx.html.  Your application acts a DMH message system client, and attaches to a running DMH server.  Once you are connected to the DMH system, you can exchange messages with other attached processes.

To get started, you only need to know the hostname where the DMH server is running, and the DMH Groupname that has been assigned to the server.   If you execute a Datahub process on your system from the command line, for example:

     $ /usr/local/bin/datahub eof &
you will start a DMH server running on your host, with the default DMH groupname of "mbx".  The windows and Linux Datahub SDK installations feature Start Menu program items for running a Datahub as a DMH server using defaults.

The usual application design, and the one that provides the best performance,  is to connect to the DMH server during initialization, and to use this connection during the life of the application.  The dmh_new() function is used to allocate a connection instance. The dmh_init( ) function is subsequently used to connect to the server.  The server's hostname and groupname are provided as arguments to the dmh_init( ) method.  If the application disconnects from the server, the dmh_init( ) command can be used again to restore the connection.

Error Handling

A long running program such as an automation application needs to handle intermittent errors with network outages or server shutdowns.

Here is a summary of the kinds of errors that the DMH functions will report.  When you make a function call that requires a DMH server connection, and you do not have one, the function call will return "No DMH server connection".  If you use an improper mailbox name such as one with whitespace in it, the function call will return a string such as, "mailbox name must contain only ANSI letters, digits, -, _, ., !, :, or @".   These are the two main errors when initiating a function call.   Most of the DMH  function calls are then processed asynchronously.  In other words, your function call returns, and the message communication you initiated happens as events are processed.  An error that occurs during event processing results in an Error event occurring.  You do not need to write an Error event handler,  but you probably should to communicate to the users of your program if an error occurs.  When the Error event occurs, in most cases, the Disconnected event will also occur.  You can place logic in your Disconnected( ) event handler to initiate recovery and resume logic, exit the program, etc.

Sending and Receiving - Basics

The programming model of the DMH message system is that you send a message to a destination mailbox.  When sending the message, you can optionally specify the name of a second mailbox for the recipient to send a reply message to.  Usually you send a message to an application logic server such as a Datahub.  The message is usually SQL,  Tcl, or VFEI command text that the recipient executes.  If the sender has designated a reply mailbox, the result of executing the command is sent to the reply mailbox.

Lets discuss sending without asking for replies using the dmh_send( ) function  or its equivalent, the dmh_put( ) function .  These functions are used to send text messages, and the dmh_binsend() function is used in a similar fashion to send binary messages.  For example,  suppose you are integrating a barcode reader.  When data is read from the barcode reader device, your code is supposed to update a record in a Datahub table.  You will send messages without asking for reply messages - if there is a system shutdown or communication failure, your application will know from the Disconnected event.  This is more efficient than asking for a reply message at every barcode read.

// update latest read record in table barcode_reader at the Datahub
char *HUB="DATAHUB";
char message[200];
sprintf(message, "update barcode_reader set data_in='%s' where device_id='%s'",
           newdata, myID);
dmh_send(dmh, HUB, message, NULL);

When you send a message without waiting for a reply, it is referred to as an asynchronous send.

Often, you will want to send a message to obtain reply data.  The most convenient function to use for text messages is the dmh_doXact( ) function.  The dmh_binXact() function is similar and is used for binary messages.  This function will take care of specifying and using a unique mailbox for your reply message, and it will take care of managing a timer in case a reply message is not forthcoming.  Suppose you want to query a database table, and the DB variable is assigned the mailbox name of the dmh_SQLsrv process.   The online documentation shows that the SQL standard "select" command sends multiple reply messages, but the "telect" command sends a single reply message with all of the requested data formatted as a Tcl list.  The dmh_doXact( ) function is designed for a single reply message, so your code looks like:

char *reply1;
char *reply2;
char msg[200];
char device_id[32];
sprintf(msg, "telect device_id from barcode_config where display='%s'", dmh_hostname(dmh));
reply1 = dmh_doXact(DB, msg);
if (!strcmp(reply1, "TIMEOUT")) {
    // timeout or error
    dmh_free(reply1);
    return "TIMEOUT";
}
// Parse the result- a Tcl List
// element(6) = rows of data, then (0) = first row, then (0) = first item in row
reply2  = dmh_listElement(reply1, 6, 0, 0);
strncpy(device_id, reply2, sizeof(device_id));
dmh_free(reply1);
dmh_free(reply2);
// success
return NULL;
 

Sending and Receiving - Advanced

If your application needs to receive unsolicited messages from other processes, you use the dmh_whenever( ) function to setup asynchronous receiving of text messages, and the dmh_binwhenever() function for binary messages.  When an unsolicited message is received, your specified Whenmsg event handler or binary message event handler is called.   By convention you should use mailbox names for receiving that end in _SQL if SQL messages are expected, _VFEI if VFEI messages are expected, and _RPC if Tcl messages are expected.   There is no limit to the number of mailboxes that you use for receiving, but the design convention is that you use unique names based on the server function(s) provided.  There should be only one receiving process per mailbox name in a DMH group.  You may wish to create a unique mailbox name for receiving by basing the name on your hostname - see the DMH function dmh_hostname( ).

The dmh_whenever( ) and the dmh_binwhenever() functions are used for ongoing receiving - your application will continue to receive the messages that are sent to your specified mailbox.  The dmh_disarm( ) function can be used to stop asynchronous receiving.

The dmh_whenmsg( ) and the dmh_binwhenmsg() functions are similar to the whenever( ) functions except that they receive only a single message.  If dmh_whenmsgAgain( ) is executed from your receiving event handler, then the software is re-armed to receive the next message.  So the combination of dmh_whenmsg( ) with dmh_whenmsgAgain( ) called in the receiving event handler is equivalent to using the dmh_whenever( ) function.

Lets revisit the dmh_doXact( ) function.  In some situations, you may want higher performance by sending messages and collecting the replies asynchronously, instead of waiting for each reply before sending the next message.  You do this by setting up one or more reply mailboxes and arming them for receiving using the dmh_whenmsg( ) or dmh_whenever( ) functions.  Instead of using dmh_doXact( ) use the dmh_send( ) command and specify the reply mailbox argument.  Typically a high performance application will create a small number of unique mailbox names for replies, and re-use them.  If you are creating unique mailboxes for each reply message, use the dmh_closeMailbox( ) function when you are done with each one, to recover resource usage.

Event Handling Notes

Your application should not "live" inside of your event handling code.  There are some rules concerning what your application can do in a receiving callback. In general, your application does not receive new asynchronous messages while your receiving callback logic is executing. So the usual application design for the callback logic is to return fairly quickly without performing lengthy computation. If your callback logic executes dmh_doXact(), dmh_binXact(), or dmh_timedReceive() then receiving of asynchronous messages is resumed during the execution of your callback. As your callback continues to execute, in the general case, there will not be re-entrant execution of your callback for receiving another message to the same mailbox. In other words, the dmh_whenever() and dmh_whenmsgAgain() logic guard against re-entrant execution of your callback logic for receiving to the same mailbox as the currently executing callback. It is possible to bypass the re-entrancy safeguard by calling dmh_whenmsg() from your callback to re-arm the callback. This is not a recommended practice.

A summary of the above paragraph; if you cause asynchronous receiving to be resumed during callback execution by using dmh_doXact() or similar functions, you should only be using dmh_whenever() or dmh_whenmsgAgain() to re-register the executing callback for the next reception. Otherwise you will have re-entrant execution of your callback.

Be careful with the Trace event.  If you turn on a lot of tracing and are exchanging long messages, your application will be manipulating huge amounts of string data.

International Character Sets

Multibyte UTF-8 character sequences are used by Tcl and DMH to represent International characters.  The API for the DMH software uses these character sequences since they are compatible with the usual single character ANSI strings used by most applications.  UTF-8 strings can be manipulated with all the usual C language functions because they do not use the null character as data.  Instead, the byte sequence 0xC0 0x80 is used to represent an imbedded NULL.

When declaring database tables to hold international text, base the VARCHAR( ) sizes on UTF-8 byte counts, not on the number of characters.  In the most conservative case,  you need to allow 3 bytes per displayed character.

Datahub table varchar fields are compatible with UTF-8 sequences representing ordinary International text.  There are issues with storing UTF-8 sequences of arbitrary binary data which can be avoided or worked around:


Signal Handling for non-Windows Systems

The DMH software creates a sending thread and a receiving thread. These threads are masked from the asynchronous signals SIGUSR1, SIGUSR2, SIGINT, SIGALM, and SIGHUP. Your application can use or ignore these signals without worrying about integration with the DMH software. The DMH software does not change the signal handling status of any thread making a library call. When your software is executing in a DMHWhenmsgProc as a result of a message being received, it is the DMH receiving thread that is executing your callback function. If you change the signal handling status of this thread in your callback logic, you should restore its original state before returning.

Termination

When your application is shutting down, call the dmh_destroy function. This call will disconnect gracefully from the DMH server and free resources used by your connection.  Practically speaking the DMH server routinely takes care of situations where clients leave ungracefully, but proper software manners are encouraged.

 

Miscellaneous Notes

An actual network connection is not attempted until dmh_init() is called.

The programming model is that you will not have more than one connection to a particular DMH server.  It is typical to have only one DMH connection per application process.  Communication across DMH groups can be accomplished by using multiple connection instances, each connected to a different DMH Group.

There should be only one reader per mailbox name in a given DMH group.

The  DMH client software is able to use and resolve DMH groupname aliases as described in the online Tcl documentation.  Groupname aliases are resolved at the DMH server and not at the client.

Mailbox Naming rules:



DMHClient API Reference

Most of the C function calls take an argument which is a pointer to a DMH Connection data structure. This argument is declared as a user defined type DMHClient. The user obtains an instance of this type by calling the dmh_new() function.

 DMHClient "Properties"
Property Access Description
void *dmh_getClientData(DMHClient)
void *dmh_setClientData(DMHClient, void *)
You can optionally set and get a per DMH Connection user-defined value.  This can be a useful mechanism to pass a pointer to an application data structure or a parent object which can then be used from any of the event callbacks.  This user-defined value is distinct from the user-defined data that you are able to associate with dmh_whenmsg or dmh_whenever callbacks.
const char *dmh_getClientID(DMHClient) The DMH server assigns a unique string to each client for identification purposes. This value is a readonly runtime value providing the server's ID string or an empty string if a connection has not been established. 
int dmh_getDefaultTimeout(DMHClient)

int dmh_setDefaultTimeout(DMHClient, int)

The default timeout interval for send and reply transactions, or timed receive invocations.   In seconds - the default value is 30.  Settable range: 1 - 86399.
const char *dmh_getDescription(DMHClient)

void dmh_setDescription(DMHClient, const char *)

One of the features of the Tcl/Tk DMH Status Window is to provide an action for identifying connected clients. The default description provided for a C DMH client is similar to "<hostname>: C DMH client". You are able to provide your own description string for your application.
const char *dmh_getmhgroup(DMHClient) The mhgroup property is a readonly runtime value indicating the hostname:port  of the DMH server when connected, else NULL.  Hostname is the server's idea of his hostname which may be different from the hostname that was specified at the client.
int dmh_getState(DMHClient)
 
 
 
 
 

 

The state property is a read-only value available at runtime.  The value can be read to determine if a healthy communication connection exists, etc.    Applications will ordinarily use event functions and not poll the state property value.  The transient and error states do not last long, so testing for the values 0 or 7 is the most common scenario. 
  0       disconnected
  4       hostname is being resolved
  6       connection setup in progress 
  7       healthy connection exists
  8       connection close in progress
  9       a communication error has occurred  
519       DMH protocol setup in progress (519 = &H100 Or 7)
int dmh_getTracebits(DMHClient)

int dmh_setTracebits(DMHClient, int)

The Tracebits property controls output of diagnostic data to the Trace event.  This value is used as a bitfield with the bit values controlling the following categories of output:

01 data reads
02 data writes
04 message receiving
08 message sending
16 logic tracing


 
DMHClient Callback Events
Event Callback  and Set Method  Description


All callback functions - NOTES
 
 
 
 
 

 

You write your own callback function consistent with the typedef of the callback.

void MyConnectedCallback(DMHClient dmh) { ... }

Then you register it:

dmh_setConnectedProc(dmh, MyConnectedCallback);

When the event happens, your callback function is called.  A pointer to the DMHClient is provided as an argument.

When registering the callback, the previous value is returned.  This lets you chain, or swap and restore callbacks.  These are not recommended techniques.

void (DMHBinmsgProc) (DMHClient, 
   const char *DestinationMailbox,
   const char *ReplyMailbox, 
   const unsigned char *Data, 
   int datalen, 
   void *ClientData);
This is the callback signature for receiving binary messages.  A callback that you write is executed by the receiving thread when a  message has arrived.  You can register different callback functions for each destination mailbox using the dmh_binwhenmsg or dmh_binwhenever functions, described in the next table. 

The DestinationMailbox parameter is a mailbox name that your application has specified when initiating receiving.  If the sender of the message indicated a reply mailbox, it is passed as the second argument, ReplyMailbox.  If no reply mailbox has been specified, the ReplyMailbox argument is an empty string.  The Data argument indicates a buffer holding the data of the sent message.  The datalen argument indicates the number of bytes in the buffer.  It is possible to send and receive messages that are 0 length.  The DMH client logic protects you from receiving another message for the DestinationMailbox, and re-entering your handler logic until you have returned from the current callback execution. 

When you call the dmh_binwhenmsg() function or dmh_binwhenever( ) function to setup receiving,  you can optionally specify the ClientData argument to be saved and passed  to your callback at the time a message arrives.  A typical use would be to  pass a pointer to a C++ object, so your receive callback can use the pointer  to call a class function. 

void (DMHConnectedProc) (DMHClient)

DMHConnectedProc * dmh_setConnectedProc(DMHClient, DMHConnectedProc *);

The Connected event happens after successfully connecting to the DMH server in the wake of the dmh_init function invocation. 
void (DMHDisconnectedProc) (DMHClient)

DMHDisconnectedProc * dmh_setDisconnectedProc(DMHClient, DMHDisconnectedProc *)

The Disconnected event happens when the DMH connection has been closed from any circumstance such as remote closure, communication failure, error, or invocation of the dmh_disconnect function.  This event is similar to the Tcl lostserver procedure invocation.  The event may happen more than once if multiple errors are being processed.
void (DMHErrorProc) (DMHClient, int errnum, const char *text)
 
 

DMHErrorProc * dmh_setErrorProc(DMHClient, DMHErrorProc *)
 
 
 
 

 

The Error event happens when the dmh_init function fails, or there has been communication failure.  In most cases when the Error event happens, the control state will transition to the disconnected state, and the Disconnected event will occur shortly.
 


Broken connection errors include:

10051  the network is unreachable
10060  Connection has timed out
11053  Connection is aborted due to timeout or other failure
10054  Connection is reset by remote system
10058  Connection has been shutdown
50001  DMH Protocol error - missing data.  You should never see this error.
50002  DMH Protocol error - improper packet.  You should never see this error.
void (DMHShutdownProc) (DMHClient, int *StayAlive)

DMHShutdownProc * dmh_setShutdownProc(DMHClient, DMHShutdownProc *)

A remote request has been received to terminate the process.  If you do not set the StayAlive flag true, the software will cause the application to exit. 
void (DMHTraceProc) (DMHClient, const char *text)

DMHTraceProc * dmh_setTraceProc(DMHClient, DMHTraceProc *)

This event provides diagnostic and debug information per the Tracebit property setting.  Your application needs to avoid creating new DMH activity in the Trace event callback, that in turn causes Trace events.  A cycle of positive feedback is possible which will cause a software fission reaction.
void (DMHWhenmsgProc) (DMHClient, const char *DestinationMailbox,  const char *ReplyMailbox, const char *Data, void *ClientData)
 
 
 
 
 
 
 

 

The Whenmsg callback is executed when a  message has arrived.  You can register different callback functions for each destination mailbox using the dmh_whenmsg or dmh_whenever functions, described in the next table. 

The DestinationMailbox parameter is a mailbox name that your application has specified when initiating receiving.  If the sender of the message indicated a reply mailbox, it is passed as the second argument, ReplyMailbox.  If no reply mailbox has been specified, the ReplyMailbox argument is an empty string.  The Data argument is the text of the sent message.  The DMH client   logic protects you from receiving another message for the DestinationMailbox, and re-entering your handler logic until you have returned from the current callback execution. 

When you call the dmh_whenmsg() function or dmh_whenever( ) function to setup receiving,  you can optionally specify the ClientData argument to be saved and passed  to your callback at the time a message arrives.  A typical use would be to  pass a pointer to a C++ object, so your receive callback can use the pointer  to call a class function. 

DMHClient Functions
FUNCTION DESCRIPTION
General Comments
 
 
 
 
 
 
 
 
 

 

Many of the function calls return NULL for the usual successful invocation, or an error message that could be logged or displayed to the user.  The most common error message is "No DMH server connection".  This message occurs when using a function that requires a connection and dmh_init( ) has not been called successfully, or the DMH connection has been lost.  The functions do not throw exceptions, and you do not need to use try and catch. 

If you use an improper mailbox name such as one with whitespace in it, the function call will return a string such as, "mailbox name must contain only ANSI letters, digits, -, _, ., !, :, or @".   Most of the DMH  function calls are then processed asynchronously.  In other words, your function call returns, and the message communication you initiated happens as events are processed.  An error that occurs during event processing results in the Error event occurring.

In order to insure threadsafe API calls, the following methods return string values that are dynamically allocated and must be freed by calling dmh_free( ) when your logic is done with the data. The memory used by your application will continue to grow unless you free these results.

  • dmh_doXact()
  • dmh_listElement()
  • dmh_listJoin()
  • dmh_listSplit()
  • dmh_serverStatus()
  • dmh_timedReceive()
For other functions, "const char *" return value declarations are common because they show that you should not tamper with the returned values - copy to save, copy to modify, etc. 
void dmh_abort(DMHClient) Any in-progress send-and-reply or modal wait transactions such as the dmh_init( ) function or dmh_doXact( ) calls are aborted with return values indicating TIMEOUT.  Invoking the dmh_abort function does not affect asynchronous receiving that is setup using the dmh_whenmsg( ) or dmh_whenever( ) functions.  Has no effect if not connected.
int dmh_bin2utf(const unsigned char *bindata, int datalen, 
 char *buffer, int buffsize)

int dmh_utf2bin(const char *utf8, unsigned char *buffer, int buffsize)

These calls are simplified conversion functions for converting between binary codes 0x00 through 0xFF and the UTF-8 strings used by the DMH and Tcl.   The sequence 0xC0 0x80 is used to represent a binary null, so the UTF-8 string representation is compatible with runtime library functions that work with null terminated strings.  These calls are used internally to support the binary messaging features, and are also available for your use.

The call dmh_bin2utf converts a binary sequence into a null terminated UTF-8 string.  The return value is the string length (strlen()) plus one for the null terminator byte - in other words the storage needed for the UTF-8 string.  Byte values from 0x01 through 0x7F only need one byte for storage, and other bytes require two.  An output buffer of size 2*datalen + 1 is able to store any binary sequence.

The call dmh_utf2bin converts a null terminated UTF-8 string into a sequence of bytes.  The conversion is aborted if the input string has invalid UTF-8 sequences, or has sequences that would map outside of the 0x00 through 0xFF range.  The return value is the number of bytes successfully converted.  In general, the output buffer size needs to be as big as the string length (strlen()) of the UTF-8 string.  The function is able to convert a UTF-8 string in place if the string buffer argument is repeated as the second argument.  (You will typically need to cast the argument type, eg., (unsigned char *).

const char *dmh_binsend(DMHClient, const char *DestinationMailbox, 
  const unsigned char *Message, int msglen, const char *ReplyMailbox)
The dmh_binsend function is used to send binary messages.  It is similar to the dmh_send function described below which is intended for UTF-8 text messages.

Sends a binary message to a mailbox, optionally with a reply mailbox indicated.   By convention, when a reply mailbox is indicated for a command message sent to a Datahub mailbox or equipment interface mailbox, the command is processed, and a reply message is sent to the reply mailbox. 

Specifying the reply mailbox as a NULL pointer, an empty string, or as the literal text "NULL" is equivalent to not specifying a reply mailbox.

Returns NULL on success, or an error message.  You must be connected to use this call.

const char *dmh_binwhenever(DMHClient,const char *ReceiveMailbox, DMHBinmsgProc *, void *ClientData) Registers to receive all messages directed to the specified mailbox.  The messages are passed to the user written callback as binary data.  When the DMHBinmsgProc callback returns, the software re-arms for receiving the next message directed to the specified mailbox.  The dmh_disarm( ) function can be used to stop receiving. 

You can optionally specify the ClientData argument to be saved and passed  to your callback at the time a message arrives.  A typical use would be to  pass a pointer to a C++ object, so your receive callback can use the pointer  to call a class function. 

You must be connected to use this call.

const char *dmh_binwhenmsg(DMHClient,const char *ReceiveMailbox, DMHBinmsgProc *, void *ClientData) Registers for receiving the next available message directed to the specified mailbox as binary data. Calling dmh_whenmsgAgain( ) in the BinmsgProc( ) event handling code re-arms the receive registration for the next message.

You can specify the ClientData argument to be saved and passed  to your callback at the time a message arrives.  A typical use would be to  pass a pointer to a C++ object, so your receive callback can use the pointer  to call a class function. 

Returns NULL on success, or an error message.  You must be connected to use this call.

unsigned char *dmh_binXact(DMHClient, const char *DestinationMailbox, const unsigned char *Message, 
 int msglen, int TimeoutSeconds, const char *ReplyMailbox, int *replylen)
Performs a complete send and reply binary transaction with timeout management.  Creates and manages a unique reply mailbox for the send and reply transaction if the ReplyBox argument is passed as NULL.  If the TimeoutSeconds argument is specified as 0, the DefaultTimeout value is used.  The usual reply is a pointer to the data of the reply message, with a non-negative number of bytes passed using the integer pointer, replylen.   In case of TIMEOUT or other failure, the pointer value will be NULL and the negative replylen value of -1 is passed.  You can have multiple instances of dmh_binXact(), dmh_doXact( ) or dmh_timedReceive( ) active at a time but not more than one instance for a specified reply mailbox.  Memory used for the reply result is dynamically allocated and passed to your application. Your logic must call the dmh_free() function with the result when you are done with it, or memory will continue to be consumed.  

If you specify a ReplyMailbox, you need to insure that the name you specify is only used by your application.  It is usual to create a unique reply mailbox name, perhaps based on the hostname and process ID, assign it to a variable, and use it repeatedly.

If you are not connected when using this call, the replylen value of -1 is returned immediately.  Trace event information can be used to distinguish failure modes.

const char *dmh_closeMailbox(DMHClient, const char *boxname) Stop using a mailbox - Disarm receiving if listening, Flush if not empty,  and remove from existence if it exists.  The Tcl version of this call, differs because it will not flush existing messages.

Returns NULL on success, or an error message.  You must be connected to use this call.

const char *dmh_count(DMHClient, const char *boxname, long *ctsent, long *ctread, long *ctpending)
Returns three numbers, the total count of messages that have been sent to the mailbox, the total count of messages that have been consumed from the mailbox, and last, the current count of pending messages.  A pending message is one that exists in the queue associated with the mailbox, and has not been consumed by reading or flushing.

Returns NULL on success, or an error message.  You must be connected to use this call.

void dmh_destroy(DMHClient) This call disconnects gracefully from the server if connected, and then deallocates the memory, threads, and resources used by the connection.
void dmh_disarm(DMHClient, const char *boxname) Un-register the listener from a specified  mailbox.  This call may be used to cancel an earlier dmh_whenever( ) or dmh_whenmsg( ) call.  If called with a NULL boxname argument, all dmh_whenever( ) and dmh_whenmsg( ) receiving registrations are canceled.  The dmh_abort( ) function will cancel in-progress dmh_doXact( ) calls.  Has no effect if not connected.
void dmh_disconnect(DMHClient) The counterpart of dmh_init( ); disconnects from the DMH server.  The dmh_send_flush( ) function gets called to complete any in-progress sends.  The dmh_abort( ) function gets called to end any in-progress transactions.  The dmh_disarm( ) function gets called to cancel all receiving.  A thread that was used for listening is caused to exit. A sending thread will be kept alive and possibly re-used. Has no effect if not connected.
char *dmh_doXact(DMHClient, const char *DestinationMailbox, const char *Message, int TimeoutSeconds, const char *ReplyMailbox)
 
 
 
 
 

 

Performs a complete send and reply text message transaction with timeout management.  Creates and manages a unique reply mailbox for the send and reply transaction if the replybox argument is passed as NULL.  If the timeout is specified as 0, the DefaultTimeout value is used.  The usual reply is the text of the reply message. The String literal TIMEOUT is returned in case of failure.  You can have multiple instances of dmh_doXact( ) or dmh_timedReceive( ) active at a time but not more than one instance for a specified reply mailbox.  Memory used for the reply result is dynamically allocated and passed to your application. Your logic must call the dmh_free() function with the result when you are done with it, or memory will continue to be consumed.

If you specify a ReplyMailbox, you need to insure that the name you specify is only used by your application.  It is usual to create a unique reply mailbox name, perhaps based on the hostname, assign it to a variable, and use it repeatedly.

If you are not connected when using this call, the TIMEOUT string is returned immediately.  Trace event information can be used to show the cause.

const char *dmh_flush(DMHClient, const char *boxname)

 

Empties a mailbox of any pending messages.  A pending message is one that has been sent to the mailbox but has not been consumed.  In other words, a pending message is waiting in a queue associated with the  mailbox name.  Messages are consumed by reading or flushing.

Returns NULL on success, or an error message.  You must be connected to use this call.

void dmh_free(void *cptr) This call frees memory that was passed to your application as the result of executing dmh_listElement( ), dmh_listJoin( ), dmh_listSplit( ), dmh_doXact( ), dmh_serverStatus(), or dmh_timedReceive( ). You can safely call dmh_free( ) with a NULL result or any other result that was passed to you from these calls. You must call dmh_free() and not your own C library free() because for performance reasons certain strings such as "TIMEOUT" and "" are static and should not be freed. The dmh_free() call is able to discern which strings are dynamic and which are static.
int dmh_groupnamePort(const char *Groupname)
Used to determine the TCP/IP port number that is used by the DMH server to listen for client connections.  The function is equivalent to the mh_name_to_socket Tcl procedure.  Most applications will not have a use for this function since the server socket port is managed by the DMH software. 
const char *dmh_hostname(DMHClient)
Returns the TCP/IP hostname of the computer that the client software is executing on.  The name is guaranteed to be stripped of domain information, and imbedded white spaces which are not valid in hostnames, are seen as delimiters of the first token.

 
 

  const char *dmh_init(DMHClient, const char *DMHGroupname, const char *DMHServerHostname)
 
 
 
 
 
 
 

 

Performs the initial connection to the DMH message server. The connection will be setup or an error result will be obtained before returning.

dmh_init errors include

10051  the network is unreachable
10060  Connection has timed out. This error indicates that the remote host may not be online.
10061  Connection attempt is refused. 
10061  Connection is forcefully rejected.
The remote host is online and reachable, but there is not a DMH server at the specified group or port. 
11001  Hostname not found (DNS authoritative).  This error may indicate an improperly spelled hostname, or a hostname that is not known to your name server.
11002  Hostname not found (non-authoritative name resolution).  This error may indicate that your name server is down.
50003  DMH Server refuses our client connection
This error indicates that customer modified software running in the DMH server has rejected the connection.  It is likely that you are in violation of your site's security policy.
50004  DMH Protocol error - improper setup reply.
You should never see this error.
If the connection succeeds, the return value is NULL, otherwise an error message is returned  indicating why the initialization failed. 

When the initialization is successfully completed, the Connected event is fired.  If dmh_init fails, depending on how the dmh_init call fails, the Error event may fire. 

If the connection to the DMH server is ever lost, the Disconnected event is fired.

Returns NULL on success, or an error message. 

char *dmh_listAppend(const char *TclList, ...)
Append one or more elements to a list.  The starting list should not be NULL; it can be an empty string.  Each argument value to the right of the initial list are text elements to be appended.  The caller MUST supply a final argument value of NULL to mark the end of the element arguments.  The first NULL element is not appended, and no elements beyond the first NULL element are appended.  For example:

  char *list =  dmh_listAppend("starting list", "first arg to add", "middle arg", "last arg", NULL);
  printf("%s\n", list);
  dmh_free(list);

Memory is allocated from the heap for the result, and it must be freed by calling dmh_free() when the caller is done with it.
char *dmh_listElement(const char *TclList, int index1, int index2, int index3); This function is similar to the lindex function of Tcl.  It will parse text formatted as a list and return the specified element.  Arguments index2 and index3 may be used to indicate that parsing of the TclList should continue up to two additional levels as a nested list structure. Or, arguments index2 and index3 may be set to -1 to indicate that parsing of the TclList as a nested list structure is not desired. If a specified index is out of bounds, an empty string is returned.  If an invalid list is parsed, NULL is returned.   Memory is allocated from the heap for the result, and it must be freed by calling dmh_free() when the caller is done with it.
char *dmh_listJoin(int argc, const char *argv[]); Joins together strings as Tcl list elements forming a result string that is a Tcl list.  Braces are added as needed to delimit empty elements, or to delimit special Tcl character sequences involving backslashes , square brackets, etc.  Memory is allocated from the heap for the result, and it must be freed by calling dmh_free() when the caller is done with it.
int dmh_listSplit(const char *TclList, int *argc, char **argv[]) dmh_listSplit( ) parses a string formatted as a Tcl list into an array of string elements.  The function understands the Tcl usage of quotes, braces and backslash sequences.  Not all strings are valid Tcl lists.  The return value is 0 on success.  Failure occurs when there are unmatched braces, unmatched quotes, or non-whitespace following braces or quotes.  Memory is allocated from the heap for the resulting argv[] stack and the elements that the stack points to. The argv result must be passed to dmh_free() to reclaim the allocated memory when the caller is done with it. The TclList passed in is treated as const - it is read, but not changed.
DMHClient dmh_new(void) This call allocates and initializes a DMH connection data structure. The return value is passed as an argument to most of the other API calls. Typically use of the DMH software begins with this call being made.
const char *dmh_product(DMHClient) The idea here is that if this interface is implemented for another product, a different string should be returned in case the using software needs to know the difference. The Hume DMH software returns a two element list with "DMH" as the first element, and a Copyright message as the second element.
const char *dmh_put(DMHClient, const char *DestinationMailbox, const char *Message) The dmh_put( ) function is equivalent to dmh_send( ) when specifying NULL as the ReplyMailbox. 
Returns NULL on success, or an error message. 
const char *dmh_putr(DMHClient, const char *DestinationMailbox, const char *ReplyMailbox, const char *Message) The dmh_putr( ) function is equivalent to dmh_send( ) with specifying a ReplyMailbox.
Returns NULL on success, or an error message. 
const char *dmh_receiveList(DMHClient) Returns a list of the mailboxes you are listening for messages on.  Does not show mailbox names that have in-progress Whenmsg callbacks.  This command may be useful for debugging, and it is not used in a typical application.  The result is an empty string when you are not connected.
const char *dmh_send(DMHClient, const char *DestinationMailbox, const char *Message, const char *ReplyMailbox);
 
 

 

Send a message to a mailbox, optionally with a reply mailbox indicated.   By convention, when a reply mailbox is indicated for a command message sent to a Datahub mailbox or equipment interface mailbox, the command is processed, and a reply message is sent to the reply mailbox. 

Specifying the reply mailbox as a NULL pointer, an empty string, or as the literal text "NULL" is equivalent to not specifying a reply mailbox.

Sending to the mailbox name "TRACE" sends the message to the DMH Server Trace Facility.

The dmh_send function corresponds to the Tcl mbx put and mbx putr commands. 

Returns NULL on success, or an error message.  You must be connected to use this call.

void dmh_send_flush(DMHClient) When you send messages, the calls return immediately and the messages are queued for sending. Since TCP/IP is relatively fast, the messages are transferred into the network layer fairly quickly. If you want to block until all of the pending send data is written to the network layer, call dmh_send_flush.
const char *dmh_serverStatus(DMHClient)
 
 
 
 
 

 

Returns a list containing the information presented in the Tcl DMH status window.  The information can be parsed by the application to determine status information on every mailbox that is currently in use.  This command may be useful for debugging, and is not used by ordinary applications.

The first element of the list is a list of 5 elements:
{ hostname:port messages_received messages_sent messages_queued tcl_version }

Subsequent elements in the list are lists of four or five elements:
{ mailboxname count_in count_out count_pending [reader_handle] }

Additional elements may exist in the list if there are DMH clients that are not currently waiting to receive messages.  These elements are formatted as:
{{{no whenmsg pending}} - - - reader_clientID}

You must be connected to use this call.

const char *dmh_timedReceive(DMHClient, const char *ReceiveMailbox, int TimeoutSeconds);
Waits for a message to be received in the specified mailbox.  If the call succeeds,  the return value is the message data.  If the call fails, the return value is the literal string "TIMEOUT".   If you are not connected, the call fails immediately with the return value "TIMEOUT".  Trace event information can be used to show the TIMEOUT cause.  If the TimeoutSeconds argument is passed as 0, the DefaultTimeout value is used. Memory used for the reply result is dynamically allocate and should be freed by calling dmh_free() when you are finished with it.
const char *dmh_version(DMHClient) Returns a two element list consisting of the DMH protocol version, and the library component configuration management Id string. Current software returns the "1.1" to  as the first element to indicate compatibility with DMH protocol version 1.1.
const char *dmh_whenever(DMHClient, const char *ReceiveMailbox, DMHWhenmsgProc *, void *ClientData)
Registers to receive all messages directed to the specified mailbox.  When the DMHWhenmsgProc callback returns, the software re-arms for receiving the next message directed to the specified mailbox.  The dmh_disarm( ) function can be used to stop receiving. 

You can optionally specify the ClientData argument to be saved and passed  to your callback at the time a message arrives.  A typical use would be to  pass a pointer to a C++ object, so your receive callback can use the pointer  to call a class function. 

You must be connected to use this call.

const char *dmh_whenmsg(DMHClient, const char *ReceiveMailbox, DMHWhenmsgProc *, void *ClientData);
Register for receiving the next available message directed to the specified mailbox.  Calling dmh_whenmsgAgain( ) in the DMHWhenmsgProc( ) event handling code re-arms the receive registration for the next message.

You can specify the ClientData argument to be saved and passed  to your callback at the time a message arrives.  A typical use would be to  pass a pointer to a C++ object, so your receive callback can use the pointer  to call a class function. 

Returns NULL on success, or an error message.  You must be connected to use this call.

const char *dmh_whenmsgAgain(DMHClient) The dmh_whenmsg( ) function functions as a one-shot.  In other words, receiving is stopped after receiving one message.  Calling the dmh_whenmsgAgain() function from the receive handler re-registers to receive the next message.

Returns NULL on success, or an error message.  You must be connected to use this call.


 


License Terms

Subject to Change Without Notice

This is licensed and supported software.
©Copyright 2011, Hume Integration Software
All Rights Reserved

The Hume POSIX DMH client software is licensed for development and runtime use at no additional charge for computers that are licensed for development use of the Hume Integration Datahub SDK or runtime use of the Hume C++ SECS libraries.   Hume Integration is also pleased to offer separate runtime licenses for using the multi-threaded C++ DMH Client software on other computer systems .


Document Version

Date of last revision: $Date: 2017/08/10 18:22:10 $

The API was updated in April 2011 when the code was revised for the latest Windows Pthreads library and 64 bit compilation using Visual Studio 2010.
  1. The dmh_listJoin method was changed to no longer require a DMHClient instance argument.
  2. The listAppend method was added.
  3. When the dmh.h header file is included by a C++ application, new DmhMutex and DmhConditionVar classes are defined.  These classes are documented with the DmhCppClient C++ library.
  4. The DmhCppClient library is provided as source code that is compiled with the user's application instead of being integrated into this C library and DLL.  This approach avoids Windows memory corruption problems that occur when sharing STL items and C++ objects across DLL boundaries where debug and release builds or compiler runtime versions may differ.
  5. The revision was built and tested for Visual Studio 2005 and newer versions.  No attempt was made for compatibility with earlier Visual Studio versions.