.NET Components for DMH Communication


Hume Integration has created two different .NET components for integrating Microsoft Windows .NET Framework applications into a distributed system using the Distributed Message Hub (DMH) message system.   The components provide 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.  The .NET components feature fully CLS (Common Language Specification) compliant APIs and can be used from any of the Microsoft .NET programming languages, including C#, C++, Visual Basic, and Java.

The original component, HumeDMH.dll, uses separate threads to implement buffered sending, and well as asynchronous receiving, and 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.  This component can provide the highest level of communication performance, however, DMH messages are delivered to the application by worker threads.  The latter is not convenient for User Interface (UI) programs because it requires message events to be passed to the UI thread in order to update the GUI.

A newer component, DmhUiClient.dll, presents the same API but delivers all events to the application thread that constructs the component.  For a UI application, this has the desirable simplicity that message handling event code can directly interact with UI features such as text displays and listboxes.  Since received messages are dispatched as events to the GUI windows event queue, there is no risk that DMH receiving logic is blocked by a deadlock in the receiving application logic. 

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 or Tcl text commands that are directly interpreted by the receiver.   To extend this ease of use to the .NET 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.
 

Choosing  the Right DMH Client Software

Hume Integration also has DMH message system client software available for other programming platforms.  Here are some remarks to help with choosing the best implementation for your requirements.

The .NET component is the software of choice for integrating new Windows applications being developed using the .NET Framework with other software processes.  If you are considering the .NET Framework for new automation applications, you should be aware that the .NET software base trades performance for runtime stability and safety.  It is not a real-time platform with deterministic performance, particularly in view of the automatic garbage collection.  On the other hand, the IDE is among the best available, and the span of runtime library functionality is truly impressive.  Its a good fit for non-realtime applications such as User Interfaces.

The POSIX C library is similar to the .NET component in that it is designed for multi-threaded applications.  The big advantage of the POSIX C library is that the same high performance API is offered on Windows and Unix/POSIX platforms.  So if you value portability, and/or the best performance, or need to integrate workstations into your system, the POSIX library is indicated.  The C API of the POSIX C library has the disadvantage that memory management is manual and somewhat tedious - the user needs to call the function dmh_free() to recover memory that is passed into the application.

The memory management disadvantages of the multi-threaded POSIX C library are overcome by using the DmhCppClient C++ library.  This library features a modern C++ API that integrates std:string and Standard Template Library (STL) classes and also integrates automatic memory management. This library is the preferred choice for newer C++ applications on both Windows and Linux (POSIX) platforms.   Hume Integration also offers an older single-threaded C++ library that can be used with Visual Studio 6.

The Java Package has great portability, and also supports multi-threaded applications.  The Java package can provide the integration glue needed to connect web-based GUI's to an automation system.  Java is also a good choice for deploying portable server logic with automatic memory management and better runtime safety.  Be careful with counting on Java for more sophisticated GUI's.  Our experience has been that Java GUI classes have been quirky with focus, activation, and repainting events.

The Visual Basic Active-X control is the software of choice for existing VB6 applications, and other platforms such as Delphi and LabView that are able to utilize Active-X controls.  The Active-X control features transparent conversion of UTF-8 and Unicode International capable character set encodings.  It also fully integrates with the IntelliSense method and parameter prompting of the IDE tools.

Don't forget Tcl/Tk - the ability to integrate with DMH messaging is built into the Tcl command set of the dmh_wish shell, so this can be your easiest and fastest deployment choice.  Tcl has excellent portability, and the GUI features (Tk) are unique in being of excellent quality and portable.  The Hume Datahub toolset includes popular third party extensions for object oriented programming with classes and inheritance, and also extensions for composite widgets such as tabbed notebooks.

Feature Summary


User Guide

The original .NET component is provided as the following files:

The files for .NET version 2.0 are distributed in the archive named DMHdotnet20.zip.  The project file is compatible with Visual Studio 2005 and is easily opened and migrated by newer Visual Studio versions.  There is a newer archive on the download site named DMHdotnet40.zip that targets the .NET version 4.0.  The newer archive is compatible with VS 2010 and does not need migration to be used by newer Visual Studio versions.  It is possible to use the HumeDMH.dll from either archive in an application that targets the same or a newer .NET Framework version.

The newer .NET component is distributed in the DotNetDmhUiClient.zip archive which contains the following files in the DMHUiClientDemo subdirectory:
In order to run the example applications, you need to have either the .NET development software installed, or the .NET runtime installed.  The .NET runtime can be downloaded from  Microsoft .  Note that the .NET runtime was not distributed with or built into older Windows operating systems.

The distribution is designed and tested for current Windows Professional versions. The component is designed to successfully interoperate with recent versions of the Tcl/Tk DMH software running on any platform.

The Hume developed .NET component is a "component" as defined by the Microsoft .NET Framework.  As such, it implements the System.ComponentModel.IComponent interface.  Hume Integration is also using the word "component" to describe high level customizable applications such as their Data Collection Component.

Installation

Installation consists of extracting files in the .zip archive to a directory or directories of your choice. 

Development

To start using the Hume .NET component with an existing project, you first add a reference to the HumeDMH.dll or DmhUiClient.dll. A C# user chooses the Add Reference... menu item from the Project menu.  From the Add Reference dialog, the Browse... button on the .NET tab notebook page is used to indicate the path to the dll.

The Hume .NET software uses the namespace Hume.DMH.  Coding is easier if you add the statement using Hume.DMH; to the using statements of your application source code.  By doing this, the namespace name, Hume.DMH, can be omitted from name references.  A Visual Basic developer uses the Imports statement for the same purpose.

Your primary focus as a developer is using the class DmhClient or DmhUiClient.  These classes implement the public methods to send and receive messages, and to work with the DMH message system.  There are other classes in the Hume.DMH namespace but they are implemented for internal use by the Dmh[Ui]Client class.

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

As a developer, per the licensing terms, you are required to have the SDK DMH 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 your own DMH server process to test and debug against without affecting a production system.

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/htm8X/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:

     C:> datahub eof 
you will start a DMH server running on your host, with the default DMH groupname of "mbx".

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.  You declare a DMH Client instance and construct it in the usual way for your chosen programming language.  To simplify the discussion, our example code will only be in C#, but the other .NET languages are similar.

    // declare and constuct our DMH connection instance
    //DmhClient dmh = new DmhClient();
    DmhUiClient dmh = new DmhUiClient();

The Init( ) method is subsequently used to connect to the server.  The Init() method is overloaded - there are two versions.  With one version, the DMH server's hostname and groupname are provided as arguments to the method.  With the other version, Init() is called without any arguments and the server hostname and groupname are configured as the properities, RemoteHost and RemoteGroup.  If the application disconnects from the server, the Init( ) method can be used again to restore the connection.

The API is organized around events, method calls, and properties.  We made a choice to feature events as the preferred means of having the DMH Client instance communicate asynchronous occurrences such as the arrival of messages.  There is superior IntelliSense code generation and prompting for developing event handlers than there is for coding method calls using delegates.  Be sure to use the += operator to add event handler callbacks to the DMH Client instance, and take advantage of the IDE's ability to code callback methods when you press the Tab key.

An application will likely want to deploy event handlers for all of the different event types that the DmhClient class raises - see the reference section and statements in the example application.  For example,

    // handle tracing and diagnostic information
    dmh.Trace += new TraceEventHandler(dmh_Trace);
    // handle incoming asynchronous DMH messages
    dmh.Whenmsg += new WhenmsgEventHander(dmh_Whenmsg);
    // handle background error notifications
    dmh.Error += new ErrorEventHander(dmh_Error);

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 many of the method invocations that return a string value report.  In the usual success situation, a null value is returned.  When you make a method call that requires a DMH server connection, and you do not have one, the method call will return the string "No DMH server connection".  If you use an improper mailbox name such as one with whitespace in it, the call will return a string such as, "mailbox name must contain only ASCII letters, digits, -, _, ., !, :, or @".   These are the two main errors when initiating a method call.   Most of the DMH  method calls are then processed asynchronously.  In other words, your method call returns, and the message communication you initiated happens in background.  An error that occurs during background 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 or  Tcl 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 Send( )  method.  This method is used to send text messages, and the BinarySend() 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
string HUB="DATAHUB";
string message =
    String.Format("update barcode_reader set data_in='{0}' where device_id='{1}'",
           newdata, myID);
dmh.Send(HUB, message);

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 DoXact( ) method.  The BinaryXact() 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 DoXact( ) function is designed for a single reply message, so your code looks like:

string reply;
string msg;
string device_id;
msg = String.Format("telect device_id from barcode_config where display='{0}'", dmh.hostname());
reply = dmh.DoXact(DB, msg);
if (reply == "TIMEOUT")) {
    // timeout or error
    return -1;
    }
// Parse the result- a Tcl List
// element(6) = rows of data, then (0) = first row, then (0) = first item in row
device_id  = dmh.ListElement(reply, 6, 0, 0);
// success
return 0;  

Sending and Receiving - Advanced

If your application needs to receive unsolicited messages from other processes, you use the Whenever( ) method to setup asynchronous receiving of text messages, and the BinaryWhenever() function for binary messages.  When an unsolicited message is received, your specified WhenmsgEventHandler or BinmsgEventHandler is called.   The Whenever() methods register to receive all subsequent messages to the specified mailbox.  When your EventHandler logic returns, you are re-registered for the next message.  The Disarm( ) function can be used to stop asynchronous receiving. If you only want to receive the next message to a mailbox, use the Whennext() or BinaryWhenmsg() methods.  If WhenmsgAgain( ) is executed from your receiving event handler, then the software is re-armed to receive the next message.  So the combination of Whennext( ) with WhenmsgAgain( ) called in the receiving event handler is equivalent to using the Whenever( ) function.

Asynchronous message receiving is setup in two parts - connecting your event handling callback to the Whenmsg event, and using the Whennext() or Whenever() method calls to specify which mailbox names you are using to receive messages.  Most applications will setup their receive message handlers once, and not vary it.  When a message arrives, you can use the WhenmsgEventArgs structure data to determine the data of the message and which mailbox name it was sent to.  The Whenmsg event sender object is the DMH Client instance.  You can optionally choose to pass a reference to an application object into the receiving event handler by using the overloaded version of Whenever() that accepts a clientData argument.

    // handle incoming asynchronous DMH messages
    dmh.Whenmsg += new WhenmsgEventHander(dmh_Whenmsg);
    dmh.Whenever("MYMAILBOX");

...
    void dmh_Whenmsg(object sender, WhenmsgEventArgs e)
    {
        Console.Writeline("Received:  To={0}  ReplyTo={1}  Text={2}},
             e.destinationMailbox, e.replyMailbox, e.message);
    }

By convention you should use mailbox names for receiving that end in _SQL if SQL messages are expected, _XML if XML 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 method hostname( ).

Lets revisit the 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 Whennext( ) or Whenever( ) methods.  Instead of using DoXact( ) use the Send( ) method 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 CloseMailbox( ) function when you are done with each one, to recover resource usage.

Event Handling Notes

With the original DmhClient class, the application cannot "live" inside of the event handling code because it is executed by the DMH receiving thread.  In general the application does not receive new asynchronous messages while receiving callback logic is executing.  So the usual application design for the callback logic is to return fairly quickly without performing lengthy computation.  This may involve passing an invocation to another thread, or adding data to a work queue.  If the receiving logic executes DoXact(), BinaryXact(), TimedReceive(), or TimedBinaryReceive() then receiving of asynchronous messages is resumed during the execution of the 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 Whenever() and 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 Whenmsg() from your callback to re-arm the callback. This is not a recommended practice.

With the newer DmhUiClient class received messages are passed into the windows event queue as they are received, so there is no longer an issue with the application possibly tying up the DMH receiving thread and blocking receiving.  During the execution of timed method calls such as DoXact() or TimedReceive(), any waiting is done without provision for servicing of the Windows event queue.  Therefore if the UI thread executes DoXact() or TimedReceive(), any asynchronous inbound messages that arrive before the desired reply message are added to the Windows event queue and are not processed until the timed method call returns and the normal handling of the Windows event queue resumes.  Similarly, when DoXact() is called by the UI thread,  Windows events are not serviced so if there is a delay, the UI is not responsive to mouse clicks, keystrokes, etc.  This potential problem is easily addressed when necessary by executing sequential DoXact logic from a worker thread and leaving the UI thread responsive to the User and asynchronous receiving.  It takes very little code to execute sequential logic from a new thread:


Be careful with the Trace event.  If you turn on a lot of diagnostic 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 the DMH to represent International characters.  The .NET DmhClient API for the DMH software uses the native string type of .NET and transparently converts between the Unicode and UTF-8 character encodings.

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:


Termination

When your application is shutting down, call the Disconnect or the Dispose method. This call will disconnect gracefully from the DMH server and free many of the 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 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 sending to mailbox@groupname.  It is also possible to use 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:



.NET DmhClient API Reference


 DmhClient Properties
Property Access Description
string ClientID 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 does not exist. 
object ClientObject You can optionally set and get a per DMH Connection user-defined value.  This can be a useful mechanism to pass a reference 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 Whenmsg or Binmsg event callbacks.
int DefaultTimeout The default timeout interval for send and reply transactions, or timed receive invocations.   In seconds - the default value is 30.  Settable range: 1 - 86399.
string Description
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 .NET client is similar to "<hostname>: .NET DmhClient Application". You are able to provide your own description string for your application.
string DmhGroup The DmhGroup property is a readonly runtime value indicating the hostname:port  of the DMH server when connected, else an empty string.  Hostname is the server's idea of his hostname which may be different from the hostname that was specified at the client.
string RemoteGroup You can specify the DMH server groupname as a property value, and call the overloaded version of Init() that takes no arguments to use the property values.  The default RemoteGroup value is "mbx".
string RemoteHost You can specify the DMH server hostname or IP address string as a property value, and call the overloaded version of Init() that takes no arguments to use the property values.  The default RemoteHost value is an empty string.  A null value or an empty string implies your own computer system..
int State
 
 
 
 
 

 

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 = 0x100 | 7)
enum Tracebits 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:

DmhClient.Tracebit.TRACE_READS  - data reads (1)
DmhClient.Tracebit.TRACE_WRITES - data writes (2)
DmhClient.Tracebit.TRACE_RECV -  message receiving (4)
DmhClient.Tracebit.TRACE_SEND-  message sending (8)
DmhClient.Tracebit.TRACE_LOGIC - logic tracing (16)
DmhClient.Tracebit.TRACE_ALL - a fast way to specify all of the above (255)


 
DMHClient Events
Event Handler and Argument Data  Description


All event handlers - NOTES
 
 
 
 
 

 

The IntelliSense code generation of the .NET IDE turns arounds the usual sequence of writing an event handling method and then registering it.  Instead, try registering an event handler using the += operator.  Press the Tab key when prompted to create event handling methods with the correct arguments.

DmhClient dmh = new DmhClient();
dmh.Connected += new ConnectedEventHandler(dmh_Connected);
...
void dmh_Connected(object Sender, EventArgs e) 
{
DmhClient dmh = (DmhClient)sender;
...
}

The .NET framework hides the difference between executing a static method or an object method as an event handler.  You are able to use either kind of method.

When the event happens, your handler method is called.  You can cast the event sender object to (DmhClient) to have a reference to the component instance.


void BinmsgEventHandler (object sender, BinmsgEventArgs e);

class BinmsgEventArgs : EventArgs {
public string destinationMailbox;
public string replyMailbox;
public byte [] data;
public object clientData;
}
 

This is the callback signature for receiving binary messages asynchronously from the Binmsg event.   A callback that you write is executed by the receiving thread when a binary message has arrived.  You typically have a single BinmsgEventHandler callback and use the destinationMailbox. data,  and/or clientData passed in the BinmsgEventArgs structure to vary your handling logic.

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 replyMailbox item.  If no reply mailbox has been specified, the replyMailbox argument is an empty string.  The data argument indicates a byte array holding the data of the sent message.  The length of the byte array is the number of bytes of message data.  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 BinaryWhenmsg() function or the BinaryWhenever( ) methods to setup receiving,  you can optionally specify the clientData argument to be saved and passed  to your handler at the time a message arrives. A typical use would be to pass a reference to a class object, so your receive callback can use the reference to call a class function.   If you do not specify a clientData argument, the passed value is null.

void ConnectedEventHandler(object sender, EventArgs e)
The Connected event happens after successfully connecting to the DMH server in the wake of the Init() method invocation. 
void DisconnectedEventHandler(object sender, EventArgs e)

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 Disconnect() method.  This event is similar to the Tcl lostserver procedure invocation. 
void ErrorEventHandler(object sender, EventArgs e)

class ErrorEventArgs : EventArgs {
public int errnum;
public string text;
}
 
 
 
 

 

The Error event happens when the Init() method 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 ShutdownEventHandler(object sender, EventArgs e) The Shutdown event happens when a remote request has been received to terminate the process.  If you do not setup your own handler for this event, the software will cause the application to exit. 
void TraceEventHandler(object sender, TraceEventArgs e)

class TraceEventArgs : EventArgs {
public string text;
}

The Trace event provides diagnostic and debug information per the Tracebits 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 WhenmsgEventHandler(object sender, WhenmsgEventArgs e)

class WhenmsgEventArgs : EventArgs {
public string destinationMailbox;
public string replyMailbox;
public string message;
public object clientData;
}
 
 
 
 
 

 

The Whenmsg event is signalled when a message has arrived that was sent to a mailbox specified in the Whennext() or Whenever() method calls.

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 replyMailbox item.  If no reply mailbox has been specified, the replyMailbox argument is an empty string.  The message 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 event handler execution. 

When you call the Whennext() function or Whenever( ) function to setup receiving,  you can optionally specify a clientData argument to be saved and passed  to your callback at the time a message arrives.  A typical use would be to  pass a reference to an object, so your receive callback can use the object  to call a class function. 

DmhClient Methods
FUNCTION DESCRIPTION
General Comments
 
 
 
 
 
 
 
 
 

 

Many of the method calls return a string value which is used in the following way to indicate success or error.  The method 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 Init( ) has not been called successfully, or the DMH connection has been lost.  In general, the functions do not throw exceptions, and you do not need to use try and catch.  The ListSplit() and ListElement() methods are different in that they will throw a FormatException if the input string cannot be parsed as a valid list.

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

void Abort(DMHClient) Any in-progress send-and-reply or modal wait transactions such as the DoXact( ) or BinaryXact() calls are aborted with return values indicating TIMEOUT.  Invoking the Abort method does not affect asynchronous receiving that is setup using the Whennext( ) or Whenever( ) methods or their binary counterparts.  Has no effect if not connected.
 string BinarySend(string destinationMailbox, 
  byte [] binaryMessage)

string BinarySend(string destinationMailbox, 
  byte [] binaryMessage, string replyMailbox)

string BinarySend(string destinationMailbox, 
  byte [] binaryMessage, int msglen)

string BinarySend(string destinationMailbox, 
  byte [] binaryMessage, int msglen, string replyMailbox)

string BinarySend(string destinationMailbox, 
  byte [] binaryMessage, int msglen, int offset)

string BinarySend(string destinationMailbox, 
  byte [] binaryMessage, int msglen, int offset, string replyMailbox)

The BinarySend method is used to send binary messages.  It is similar to the Send method described below which is intended for text messages.  There are 6 overloaded varieties depending on your desire to specify optional parameters.  The optional parameter offset lets you specify a starting element in the byte [] array that is different than the initial byte at offset 0.  The optional parameter msglen lets you indicate the number of bytes in the message in the case where it is less than the length of the byte [] array.  The replyMailbox parameter is optionally specified to indicate a reply mailbox which is passed along with the message data to the receiver.  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 replyMailbox as null, 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.

string BinaryWhenever(string receiveMailbox)

string BinaryWhenever(string receiveMailbox, object clientData)

Registers to receive all messages directed to the specified mailbox.  The messages are passed to the user written Binmsg event handler as binary data.  When the callback returns, the software re-arms for receiving the next message directed to the specified mailbox.  The Disarm( ) method can be used to stop receiving. 

You can optionally specify the clientData argument to be saved and passed  to your event handler at the time a message arrives.  A typical use would be to  pass a reference to an object into your receive handler.

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

string BinaryWhenmsg(string receiveMailbox)

string BinaryWhenmsg(string receiveMailbox, object clientData)

Registers for receiving the next available message directed to the specified mailbox as binary data. Calling WhenmsgAgain( ) in your Binmsg event handling code re-arms the receive registration for the next message.

You can optionally specify the clientData argument to be saved and passed  to your event handler at the time a message arrives.  A typical use would be to  pass a reference to an object into your receive handler.

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

byte [] BinaryXact(string destinationMailbox, byte [] binaryMessage)

byte [] BinaryXact(string destinationMailbox, byte [] binaryMessage, int msglen, int offset)

byte [] BinaryXact(string destinationMailbox, byte [] binaryMessage, int timeoutSeconds)

byte [] BinaryXact(string destinationMailbox, byte [] binaryMessage, string replyMailbox)

byte [] BinaryXact(string destinationMailbox, byte [] binaryMessage, int timeoutSeconds, string replyMailbox)

byte [] BinaryXact(string destinationMailbox, byte [] binaryMessage,  int msglen, int offset, int timeoutSeconds)

byte [] BinaryXact(string destinationMailbox, byte [] binaryMessage,  int msglen, int offset, int timeoutSeconds, string replyMailbox)

Performs a complete send and reply binary transaction with timeout management.  Optionally creates and manages a unique reply mailbox for the send and reply transaction if the replyBox argument is not specified.  If the optional timeoutSeconds argument is not specified, the DefaultTimeout property value is used.  There are overloaded varieties to let you specify when the binary message does not start at the beginning offset of 0, or when it's length is less than the length of the binaryMessage array.

The usual reply is a byte array containing the reply message.  The allocated size of the byte array is the length of the message and it can be 0.   In the case of TIMEOUT or other failure, the return value will be null.  You can have multiple instances of BinaryXact(), DoXact( ),  TimedReceive( ), or TimedBinaryReceive() active at a time but not more than one instance for a specified reply mailbox. 

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 null value is returned immediately.  Trace event information can be used to distinguish failure modes.

string CloseMailbox(string mailbox) Stops using a mailbox - disarms asynchronous receiving, aborts modal receiving, flushes the mailbox, and removes it from existence.  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.

int [] Counts(string mailbox) Returns an array of 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.

You must be connected to use this call.  If you are not connected, or an error occurs, the return value is null instead of the array.

void Disarm()
void Disarm(string mailbox)
The Disarm() method is used to end asynchronous receiving that was initiated with the Whenever(), Whennext(), BinaryWhenever(), or BinaryWhenmsg() methods.
If called without a boxname argument, all asynchronous receiving registrations are canceled.  The Abort( ) function is similar and is used to cancel in-progress DoXact( ) and BinaryXact() calls.  The call has no effect if not connected.
void Disconnect() The counterpart of Init( ); this method is used to disconnect from the DMH server.  The SendFlush( ) function gets called to complete any in-progress sends.  The Abort( ) function gets called to end any in-progress transactions.  The Disarm( ) function gets called to cancel all asynchronous receiving.  Has no effect if not connected.
void Dispose() Disconnects from the DMH Server and frees all Windows resources.  Called automatically by the destructor.
string DoXact(string destinationMailbox, string message)

string DoXact(string destinationMailbox, string message, int timeoutSeconds)

string DoXact(string destinationMailbox, string message, string replyMailbox)

string DoXact(string destinationMailbox, string message, int timeoutSeconds, string 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 replyMailbox argument is not specified.  If the timeoutSeconds is not specified, or specified as 0, the DefaultTimeout property 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 DoXact( ) or TimedReceive( ) or their binary counterparts active at a time but not more than one instance for a specified reply mailbox. 

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.

string Flush(string mailbox)

 

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.

int GroupnamePort(string groupname)
Used to determine the TCP/IP port number that is used by the DMH server to listen for client connections.  The function is similar 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. 
string hostname()
Returns the TCP/IP hostname of the computer that the client software is executing on. 

 

string Init()

string Init(string groupname, string serverHostname)
 
 
 
 
 
 
 

 

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

If called with no arguments, the property values RemoteGroup and RemoteHost are used.  The server hostname can be specified as a TCP/IP hostname, or as an IP address string.

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 occurs.  If Init fails, depending on how the Init call fails, the Error event may occur. 

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

StringBuilder ListAppend(string list, string element);

StringBuilder ListAppend(StringBuilder list, string element1);

StringBuilder ListAppend(StringBuilder list, string element1, string element2);

StringBuilder ListAppend(StringBuilder list, string element1, string element2, element3);

StringBuilder ListAppend(StringBuilder list, string element1, string element2, element3, element4);
 

These methods are used to add one to four list elements to text that is formatted as a Tcl list.  It is a good programming practice to use ListAppend or ListJoin to build a Tcl list, in order to make sure that imbedded white space or other special characters are properly delimited with curly braces or escaped with backslashes.  A null value may be passed as any of the string argument values, in order to represent an empty list or empty element.  However, a null value should not be passed as a System.Text.StringBuilder argument.  The System.Text.StringBuilder class is designed to support more efficient string modification than using instances of the string class.  The input StringBuilder objects are modified by reference and returned as the return value of the methods.  You can construct a StringBuilder instance that does not contain any characters to represent an empty list.   The overloaded method calls make it convenient to add up to four list elements in one call.  If you need to add more elements, call the methods repeatedly.
string ListElement(string list, int index1);

string ListElement(string list, int index1, int index2);

string ListElement(string list, int index1, int index2, int index3);

This function is similar to the lindex function of Tcl.  It will parse text formatted as a Tcl 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. If a specified index is out of bounds, an empty string is returned.  Not all strings are valid Tcl lists.  If an invalid list is parsed, the method call throws the FormatException.
string ListJoin(string [] 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. 
string [] ListSplit(string list) 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.  If an invalid list is parsed, the method call throws the FormatException.  Failure occurs when there are unmatched braces, unmatched quotes, or non-whitespace following braces or quotes.
string Product() The idea here is that if this API 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.
string [] ReceiveList() Returns a string array of the mailboxes you are listening for messages on.  Does not show mailbox names that have in-progress event callbacks.  This command is useful for debugging, and it is not used in a typical application.  The result is an empty array when you are not connected.
string Send(string destinationMailbox, string message);

string Send(string destinationMailbox, string message, string 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 null, 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. 

See the BinarySend() method to send binary data.

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

void SendFlush() 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 this method.
string ServerStatus()
 
 
 
 
 

 

Returns a string value formatted as a Tcl 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 is useful for debugging, and is not used for ordinary application logic.

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.

string TimedReceive(string 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. 
byte [] TimedBinaryReceive(string receiveMailbox, int timeoutSeconds); Waits for a binary 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 null.   If you are not connected, the call fails immediately with the return value of null.  Trace event information can be used to show the failure cause.  If the TimeoutSeconds argument is passed as 0, the DefaultTimeout value is used. 
string Version() Returns a string value formatted as a two element Tcl 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.
string Whenever(string receiveMailbox)

string Whenever(string receiveMailbox, object clientData)

Registers to receive all messages directed to the specified mailbox.  When a message is received, a Whenmsg event occurs.  When the event handling callback returns, the software re-arms for receiving the next message directed to the specified mailbox.  The Disarm( ) function can be used to stop receiving. 

You can optionally specify the clientData argument to be saved and passed  to your event handler at the time a message arrives.  A typical use would be to  pass a reference to an object. 

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

string Whennext(string receiveMailbox);

string Whennext(string receiveMailbox, object clientData);

Register for receiving the next available message directed to the specified mailbox.  When a message is received, a Whenmsg event occurs.  Calling WhenmsgAgain( ) in the event handling code re-arms the receive registration for the next message.

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 reference to an object. 

Note - this method would have been named Whenmsg() to preserve the correspondence with other DMH software, except that the Whenmsg name is being used as the event type name. 

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

string WhenmsgAgain() The Whennext( ) function functions as a one-shot.  In other words, receiving is stopped after receiving one message.  Calling the WhenmsgAgain() function from the receive handler re-registers to receive the next message.  This method is available for the DmhClient class where the app handling logic is executed by the DMH receiving thread and the Whennext context is known.  The method is not supported by the DmhUiClient since messages are passed off to the UI thread and the Whenmsg context is lost.

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


License Terms

Subject to Change Without Notice

The Hume .NET 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.  We ask that developers install the Tcl executables and actively use the Tcl executables for testing and development, instead of developing against production servers.  Also, we ask that developers install the Tcl online documentation and use it to supplement the material presented in this document.

Hume Integration is also pleased to offer separate runtime licenses for using the .NET Client software on systems that are not licensed as development systems.  In other words, runtime usage of the .NET Client software is licensed separately from the Datahub SDK runtime license.


Document Version

Date of last revision: $Date: 2018/11/09 17:39:51 $