WuPiN G XiN BloG

It is All about Traffic, and Programming

Search

Archives

0 visitors online now
0 guests, 0 members

Dissecting NTCIP 1202/SNMP Message – Part 1

The National Transportation Communication for ITS Protocol (NTCIP) is a family of standards for electronic traffic control equipment.  For traffic engineers, or professionals that have to deal with traffic signal control,  NTCIP 1202 is of particular relevance as it defines the data elements and conformance requirements for actuated traffic signal controllers (ASC).

NTCIP 1202 specification document and accompanied MIBs can be downloaded from http://www.ntcip.org/library/standards/?documents=yes&standard=1202

Looking at NTCIP 1202 spec,  various objects are defined in the document in a descriptive, high-level language.  However, in order to do actual NTCIP 1202 related programming and implementation either as a application developer or system architect/designer, or some other signal control hardware-related work,  a *detailed* binary-level understanding of SNMP message is in order. 

This article serves as a personal study note on some of the key points for understanding NTCIP 1202 SNMP messages.  I have had some sort of solid years of experience in computer engineering (hardware and software), and have been working in Traffic Engineering/ITS industry for a couple of years. So I guess this post would be most helpful for someone that has a similar background , while wishing to grab a jump start to some low-level programming and development work dealing with 1202/SNMP messages.

Also I would like to thank Mr. Kenneth Vaughn (an NTCIP expert, of Trevilon Corporation) from LinkedIn NTCIP group, for some of his clarifications and explanations that had helped me a lot while I educated myself on this subject.

To start with,  let’s check out the big picture  about NTCIP framework, which is best exemplified in Figure 1. As shown in this Figure,  the framework has several levels, from the bottom physical levels up to  the topmost information level.  From top most level to the bottom level,  lower level will always add some wrappers (headers) to the higher level information.  We are only interested to levels up to Application Level, which, in the context of this post, is NTCIP 1202 SNMP message.


(Figure 1: NTCIP Framework. source: http://www.ntcip.org/library/protocols/)

(Figure 2: Fields in SNMP Message.  Source: http://www.rane.com/note161.html)

(Figure 3: A sample SNMP Message.  Source: http://www.rane.com/note161.html)


An  SNMP message contains a sequence of raw binary bytes, formalized in a well-defined structure (which contains several fields), as exemplified in the above Figure 2.   Figure 3 shows an actual sample of SNMP message:

1. A message starts with 0x30, indicating this is a “Sequence” type message

2.For encoding OID, the node is encoded using the 7 low-order bits of each byte. The high-order bit indicates whether there are more bytes to follow within the node number.

For example, 0x89 0x36, => 1000 1001 (high bit set, look for another byte) 0011 0110 (high bit not set, this is end). Removing high order bits, we get 000 1001 011 0110.
Putting back into two bytes 0000 0100 1011 0110, which is 0x04 0xB6 = 1206 decimal
(thanks to Kenneth Vaughn for explaining and clarifying these to me)

3. The second byte, if the highest bit is not set, i.e., 0, then the byte itself would indicate the Length (number of bytes) that follows in this message. If the highest bit is set as 1, then the low 7 bits indicate the number of bytes that follows which specify the Length of the message.

The length field is encoded as a BER length. The maximal value for this encoding is a length field that starts with the value 0xFE (0xFF is reserved) so the maximal length would be defined by a 126 byte integer, or 2^1008 – 1. (thanks to Kenneth Vaughn for explaining and clarifying these to me).

For example, 0x2C means a total of 44 bytes will follow (the entire length of the whole message, would be 44 + 2 = 46 bytes considering the first 2 bytes 0x30 0x2C)

0x82 0x01 0xB0 would indicate the length is expressed in 2 subsequent bytes, since with 0x82 the highest bit is set as 1. And that gives 0x01B0 = 432 bytes, and therefore the total number of this SNMP message would be 432 + 4 = 435 bytes.

4. Then we have the part on SNMP Version, following the format Type, Length, Value

For example, 0x02 0x01 0x00 means, Integer Type, of 1 byte, value is 0, i.e., SNMP version 1.

5. The following lists the common ASN.1 data types, relevant to SNMP:

Primitive Types

ID

Complex Types

ID

Integer

0x02

Sequence

0x30

Octet String

0x04

GetRequest PDU

0xA0

Null

0x05

GetResponse PDU

0xA2

Object ID

0x06

SetRequest PDU

0xA3

 

Hushing the nagging screen from Syncrho Education Version

Synchro Education Edition – has a nagging screen EACH time the software is launched – Until you click “Accept”,  you will be going nowhere.

After “Accept” is clicked,  you will see the main UI water-marked with the line saying “Educational Use Only”.  Also you will experience a noticeable freezing period up to 1 or 2 seconds – the software is surreptitiously checking with a Trafficware server in Texas for any new version available, and verifying the license information.

This Fall semester I am teaching a Traffic Control & Simulation course at NYU-Poly, and get to use the Synchro academic version with some sort of frequency.  This nagging screen is becoming quite annoying to me – not to mention personally I tend to attribute this nagging trick as being somewhat paranoid and pointless, especially when the actual protection with Syncrho is shockingly weak.

I simply don’t like that each time I have to manually click the “Accept” button, in order to proceed. Don’t get me wrong,  this is not that I disagree with the terms,  just I don’t like manually doing it.  I would like to make a tool, and authorize the tool,  representing myself, to Agree and Accept to the license terms by clicking the “Accept”  button for me.

Therefore – with my tool of choice – AutoIt,  I prepared a little nifty script so that each time the software is launched, the computer itself will automatically click the button for me to Accept the terms – I don’t need to bother to move any of my fingers.

Wowooo – La!

The following is download link to the compiled version of the script, just put it anywhere on your computer’s desktop to replace the default Synchro shortcut lnkYou are still going to see the nagging screen appear, so you know what you are going to Accept,  but immediately it will be closed since the script, as authorized by you, and truly you,  automatically click the “Accept” button for you.  Besides,  the version checking window will be immediately closed as well,  and you won’t see the initial 1 or 2 seconds freezing period.  You can always to go to “Help” -> “Check for Updates” to do the explicit version check, yourself.

http://www.wupingxin.net/wx-files/synchro-8.7z

Midtown In Motion Covers More Intersections and Wins National Innovation Award

New York City Department of Transportation today announced the expansion of Midtown in Motion, the innovative congestion management system deployed in Manhattan since last year.

The first phase of Midtown in Motion has resulted in an overall 10% speeds improvement as suggested by the E-ZPass travel time data and taxi GPS data.   The initial deployment for real time congestion control included 100 microwave sensors, 32 traffic video cameras and E-ZPass readers at 23 intersections to measure in real time traffic speeds,  travel times,  traffic counts and occupancies.

This new expansion of Midtown in Motion will have  a total of 210 microwave sensors, 56 traffic cameras and E-ZPass readers at 59 intersections, covering the Midtown Manhattan area from 1st to 9th avenue and from 42nd to 57th street,  more than 270 square blocks  (more than 300 controlled intersections) .  The collected real time data are fed to sophisticated algorithms and signals are updated accordingly (operator-in-loop or autonomously).

Midtown In Motion has won ITS America Smart Solution Spotlight Award , in recognition of New York City’s commitment to “smart” technologies that allow engineers to respond to traffic conditions in real time.

 “From cameras to microwave sensors and EZ-pass readers to Advanced Solid State Traffic Controllers, Midtown in Motion is a showcase of the most sophisticated intelligent transportation solutions available to public agencies. ITS America is excited to recognize this comprehensive use of technology and real time data which has resulted in a highly successful deployment that can be replicated in cities throughout the country.”  – Scott Belcher, President and CEO of ITS America.

Details of the Press Release are available at the NYCDOT website and the ITS America website

http://www.nyc.gov/html/dot/html/pr2012/pr12_25.shtml

http://itsa.org/component/content/article/1-general/1431-nycs-midtown-in-motion-receives-national-transportation-award

QThread: the Way of Do-It-Right

Recently I run into this blog of Bradley T. Hughes, a member of the Qt development team.   He put an interesting post about the “right” way of using QThread.  That is,  to sub-class QObject and use  moveToThread.  This is quite contrary to what is said in the existing Qt official documentation, which suggests subclassing QThread directly.

The reason of doing this is that QThread should be treated as an interface,  a control point or a manager of worker threads.  As such  it shouldn’t contain  any threaded code.   As a matter of fact,  QThread’s thread affinity by default is with its creating thread,  not the thread it spawns.  This means QThread by default shares the same thread message queue as its creating thread, which in most cases is the main thread.

There are a few discussions on the internet about subclassing QThread,  while calling moveToThread in the QThread sub-class’s constructor.  This is based on the misconception of QThread usage.  Sometimes it works as a work-around trick to change the default thread affinity from the creating thread to the thread it spawns.  This practice is not encouraged,  according to Bradley blog, because this  will result in not only a bad design violating OO principles but also problems very hard to debug later.  This is well explained in Bradley’s blog.

Here is a brief summary of the right (suggested) way of using QThread:

1. When no event-loop is involved,  we can continue with the “official” approach, like what is introduced in the Qt documentation, i.e.,

  • Subclass QThread;
  • Overload Run();
  • Use some termination token inside a while loop inside Run(), so it can exit gracefully.

2. When event-loop is involved, things are a little more complicated:

  • Subclass QObject, define relevant signals and slots.  Distribute the “threaded” code over the relevant slots;
  • Since QThread::run() automatically calls  QThread::exec() so no need to worry that (for Qt 4.4 and later);
  • Call QObject::moveToThead, so that the slots are executed in the context of the working threads spawned and managed by the QThread instance.

Maya Posch’s blog gives a nice example illustrating the the above:

class Worker : public QObject
{
    Q_OBJECT
public:
    Worker();
    ~Worker();
public slots:
    void process();
signals:
    void finished();
    void error(QString err);
private:
    // add your variables here
};

// --- CONSTRUCTOR ---
Worker::Worker()
{
   // you could copy data from constructor arguments to internal variables here.
}

// --- DECONSTRUCTOR ---
Worker::~Worker()
{
   // free resources
}

// --- PROCESS ---
// Start processing data.
void Worker::process()
{
   // allocate resources using new here
   qDebug("Hello World!");
   emit finished();
}

In Maya’s original example,  the following is posted:

QThread* thread = new QThread;
Worker* worker = new Worker();

worker->moveToThread(thread);

connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));

// The following line may cause memory leak.
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();

There are some nuances here –

First,  the highlighted line may cause leak of the worker instance,  because the the QThread instance may have already be deleted earlier. Then the thread context is no long existing for the following line of code since it is only executed in the context of the thread managed by QThread:

connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));

Therefore,  this line

connect(thead, SIGNAL(finished()), thread, SLOT(deleteLater()));

should be changed to

connect(worker, SIGNAL(destroyed()), thread, SLOT(deleteLater()));

Second,  do NOT allocate resources in the ctor of QOjbect derived class.  This is because before calling moveToThread,  the thread affinity is still with the main thread (i.e., the thread where the instance of the derived class is created) hence it may cause some hidden hard-to-debug problems later with the switch of thread affinity.

REFERENCE LINKS 

  1. http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
  2. http://labs.qt.nokia.com/blogs/2006/12/04/threading-without-the-headache/
  3. http://labs.qt.nokia.com/blogs/2007/07/05/qthreads-no-longer-abstract/
  4. http://gitorious.org/qthreadhowto/qthreadhowto/trees/master
  5. http://blog.exys.org/entries/2010/QThread_affinity.html
  6. http://thesmithfam.org/blog/2010/02/07/talking-to-qt-threads/
  7. http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
  8. http://hi.baidu.com/cyclone/blog/item/5fac3bc7ab1b90d1d10060f2.html

Advanced Aimsun API Programming (1)

Aimsun MicroAPIs are the programming interface of Aimsun microscopic traffic simulator. These APIs provided 10 predefined call back hooks, together with various other utility functions.

MicroAPIs allow advanced users to implement sophisticated external logic, for example, non-standard ramp metering, advanced traffic management strategies, adaptive signal control etc.. These are not readily available with standard off-the-shelf Aimsun features, but made possible with Aimsun MicroAPIs.

The Aimsun MicroAPIs are very flexible, as its final product is in the format of a Dynamic Link Library (DLL). The DLL is loaded by Aimsun host during run time before running a simulation replication, and unloaded after the simulation replication finishes. The supported programming language is C++ and Python. But a developer with sufficient knowledge of DLL programing can easily port the development in other languages such as Delphi, C#, Java, Fortran, if he or she desires so.

One nuance about Aimsun MicroAPIs programming  is the granular control level down to a single simulation step. This is no surprise since Aimsun Micro is a time-discretized space continuous simulator that all the simulation events have to be driven by incrementing simulation steps.  However,   this might pose some programming challenges or certain pitfalls when multi-threading is involved, or  when a TCP/IP based client-server structure is employed.  The tricky point is that at the simulation step level,  the control of flow is transferred from Aimsun host to MicroAPI DLL,  while NOT until the call back finishes,  Aimsun host is essentially “blocked”. This could possibly counter-balance any benefits obtained from the multi-threaded code implemented inside the API logic.

Overall, Aimsun MicroAPIs include two types of functions:

  1. Predefined DLL Exported Functions (hooks to the Aimsun Host);
  2. Utility Functions provided by the Aimsun Host to retrieve various run time information.

The utility functions can be further classified into three categories:

  1. External Control Interface Functions for traffic signal control (ramp metering, intersection signal control etc). These functions are usually prefixed with ECI, for example, ECIChangeParametersFlowAlineaMeteringById;
  2. Aimsun NG Objects Connection Interface Functions for accessing properties of Aimsun internal objects. These functions are usually prefixed with ANGConn, for example, ANGConnActivatePolicy;
  3. Aimsun Kernel Interface Functions to retrieve other simulation run time information. These functions are usually prefixed with AKI, for example, AKIDetGetDensityAggregatedbyId.

Predefined Callbacks

As mentioned above, Aimsun MicroAPIs provided 10 predefined call-back functions (hooks). These functions are exported by the MicroAPI DLL, so that the Aimsun host can load the DLL and call these functions at appropriate time during simulation. It is the task of the MicroAPI developers to design the external logic and implement the logic under the framework of these functions properly.

In a nutshell, Aimsun host will call back these functions on the following events:

  1. The MicroAPI DLL is loaded or unloaded by the Aimsun host;
  2. The external logic is registered with the Aimsun host;
  3. Before running, or after finishing the simulation;
  4. Before or after a simulation step is executed;
  5. A vehicle enters or exits the transportation network;
  6. Before a route choice calculation is performed.

In addition to the 10 predefined call-backs (See Table 1 below)  exported by the MicroAPI DLL, there are also many utility functions exported by the Aimsun host. This renders two way communication between the MicroAPI DLL and the Aimsun host for exchanging of information and control.

As to be explained in future post,  Aimsun’s two way communication is quite different from VISSIM, another popular traffic simulator developed and marketed by the Germany company PTV AG.  VISSIM also provides various APIs, e.g., VISSIM external driver APIs,  VISSIM signal control APIs etc.   Particularly, VISSIM provides an automation mechanism by virtue of Microsoft’s COM technology.   Regardless,  in terms of APIs,  VISSIM is merely one way communication.  The pros and cons of these two types of interfaces will be discussed later.

To use the utility functions exported by the Aimsun host, “angext.lib” must be specified for the linker.  However,  for some reason,  the corresponding DLLis not named angext.dll,  but a2kernel.dll.

The following table summaries the 10 predefined call-backs.

TABLE 1. Aimsun MicroAPIs 10 Call-backs (hooks)

Lane Closure Utility for VISSIM 5.40 (32bit and 64bit)

The “Lane Closure Utility” is a hobby tool I developed when I was a graduate student 5 years ago.  It utilizes VISSIM COM interface to dynamically close or open any lane of any link at any time,  during the simulation.

The tool can be pretty handy for modelling lane closure (You Bet!),  incidents,  evacuation contraflow, work zones etc.

Interestingly,  over the years PTV didn’t bother to implement this “dynamic lane closure” as its default feature.  Once you specify a lane closure at the beginning,  it will be literally a lane closure throughout the entire simulation period.  This “limitation” might be awkward sometimes,  if you do not mean to close the lane for the entire simulation period,  but just want to close the lane at certain point,  and re-open it at a later time during the simulation.

Since VISSIM does not provide a convenient off-the-shelf solution for this type of situation,  there have been surprisingly quite some VISSIM users,  including university faculties, graduate students,  traffic engineers contacted me for accessing this utility,  to use in their engineering projects,  research dissertations and others.

As of today,  the latest VISSIM version available to North American users is 5.40 (SP3).   I just updated this small utility to be compatible with this latest VISSIM 5.4 version.  Because PTV won’t change VISSIM COM Lib’s GUID with its service packs,    the utility should work with all service packs of the same major version.

The updated lane closure utility can be downloaded from the following link:

vissim54-lcu-x86-x64

A small tutorial:

LCU Tutorial

The utility is built both as 32 bit and 64 bit programs, to work with respective VISSIM 32 bit or 64bit.  The download package includes a little demo and a quick video tutorial (you might need to install tscc CODEC to play the video, though).

The following is the snapshot of the user interface.

To set the schedule of a lane closure event,  click “New” under “LCU File Items”.  You will see the following

A lane closure event is defined as an ensemble of the following elements:

Scheduled Time, in seconds counted from the simulation start time;
Event Type: Lane Closure, or Lane Open;
Link ID;
Lane ID;
Affected Vehicle Class List.

This boils down to the following lane closure configuration file:

20-LaneClose-1-1-2-10-20-
40-LaneClose-1-2-2-20-10-
60-LaneOpen-1-2-2-10-20-
1000-LaneOpen-1-1-2-10-20-

Essentially, the above configuration says:

at time 20 seconds,   close Lane 1 of Link 1 to Vehicle Class 10 and Vehicle Class 20
at time 40 seconds,   close Lane 2 of Link 1 to Vehicle Class 10 and Vehicle Class 20
at time 60 seconds,   open  Lane 2 of Link 1 to Vehicle Class 10 and Vehicle Class 20
at time 1000 seconds, open  Lane 1 of Link 1 to Vehicle Class 10 and Vehicle Class 20

After specifying the schedule,  load the VISSIM network file and click “Run VISSIM”, then VISSIM will be launched.  You can control the simulation speed by sliding the “Simulation Speed” track bar. That is all.  After all,  it is a very simple tool anyway.  If you find it useful or interesting,  or just sucks,  please don’t hesitate to drop me a “thank you” message or tell me “it stinks” or whatever.

Delphi exception, Fortran mystery

A mysterious exception,  which  after I scratched my head for quite a while,  turned out to be a simple overlook.

In a recent testing of a new internal beta release of some software , we came across a fatal exception thrown from the main program by the end of the simulation run. It happened when the program was trying to export the data to a network SQL server.

What was perplexing is that the exception only happened on testing machines;  on the developer’s machine,  everything seemed normal.

After pulling some of my hairs, it turned out –

On the development machine,  we have the capacity-limited SQL Server Express 2008 R2 installed for debugging and testing purpose.  When developing,  this  server is restricted as a local server and set as the default database connection. The system has a separate ini file that  overwrites the default setting and specifies the real database server to connect.

The SQL server connection instance, when created and initialized with its ctor,  uses the default connection set up, which will try to create a connection in the first place to the local SQL server on the development machine. Something like this:

[delphi]

lvSQLConn := TMSSQLConnection.Create;

try
try

lvSQLConn.ServerAddress := (server address specified from the ini file)
except

end;
finally
lvSQLConn.Free;
end;
[/delphi]

Therefore the default connection happens during the construction.  Of course,  when running on  the development machine,  there will be absolutely no problem,  because that local SQL server will always be available to access as Named Pipe.   However, on the deployment machine,  that local SQL server becomes inaccessible, hence an exception (something like “Server doesn’t support requested protocol“) is thrown right in the middle of construction, which as you  can see from the code, is not caught by the try-except-finally, but caught by its host, which has no idea about the type of exception.

The fix is very simple and straightforward:

  •   Move the class construction line into the try-except-finally;
  •   Turn off the default connection (i.e., set the Active := False for the data access component during design time).

The morale of this story?

  • Be wary of (or try not to do) resource allocation during an object construction;
  • Be careful with class constructor so exceptions would not be thrown, or if thrown, get properly handled.

Use AutoIt Scripting to automatically Connect and Disconnect VPN

Assume a business model uses two database servers,  one is master, behind a corporate firewall; the other is a replication behind another firewall.  At midnight,  a one-way replication will be performed to sync the master database server with the replication one.  Before the synchronization is performed, a VPN connection needs to be established, and the VPN connection will be released after the synchronization is done.

Autoit at  http://www.autoitscript.com/site/autoit/ provides a very neat solution to automate this.  To to  that,  first create an Autoit script, then compile the script as an executable,  and schedule the executable  as a Windows task.

In order to make this work,  the scheduled task MUST be set as “Run only when user is logged on”.  This is because if not,  Windows OS will run the program in the background and Autoit cannot grab the window’s handle therefore any key strokes emulation will not work.

In summary,

  • Download AutoIt
  • Create a script like this:

[sourcecode]
Run("..\Cisco Systems\VPN Client\vpnclient.exe connect user aa pwd bb ")
Sleep(5000)
ControlSend("[CLASS:WinConsoleClass", "","","y")
Sleep(1000)
RunWait("dbsync.exe")
Run("C:\Program Files (x86)\Cisco Systems\VPN Client\vpnclient.exe disconnect")
[/sourcecode]

  • Use AutoIt compile the above script.
  • Set a scheduled task with Windows task scheduler, with “Run only when user is logged on” option selected.
  • Locked the user account for which the above task is scheduled.  This is important.  You must NOT log off because then the task will not be run (even you set it as “Run whether user is logged on or not“).  The reason is that,  when the CISCO VPN client connection is running in the background (i.e., “Run whether user is logged on or not” is selected),  it would NOT receive emulated key strokes to accept the connection.  As a result, the scheduled task will be launched but it will not continue and be hanging there forever.

In the above script,   RunWait command is used to launch the database synchronization program dbsync.exe.  Then the script execution waits till dbsync.exe finishes, before disconnecting the VPN connection.

Also noted that “ControlSend” is used instead of “Send” to send the emulated “y” key stroke,  as in the following script line:

[sourcecode]
ControlSend("[CLASS:WinConsoleClass", "","","y")
[/sourcecode]

Without the emulated “y”,  the CISCO VPN client will simply hang there forever,  waiting for the user’s confirmation.

The difference between “ControlSend” and “Send” is Send is going to send an emulated key stroke to the active window,  while ControlSend  to any window specified with its class title.  Send will not work with the logged user locked, since in that case there is no “active” window as seen by Autoit.

Before closing, there is one more point to note – remember not to open more than 1 cmd window.  This will confuse ControlSend, and the keystroke might be sent to the other cmd window than intended.

 

Note on OmniThreadLibrary (OTL): Communication Channel

To facilitate communication between two thread task workers,  OmniThreadLibrary provides a very handy mechanism, i.e., communication channel.   Two different task works, each can register the one end point of the channel, and communicate through that channel like this:

Taken from demo 8 RegisterComm,

In the main thread, create the communication channel, the task controls, and associate the two end points of the channel with each of the task worker:


procedure TfrmTestRegisterComm.FormCreate(Sender: TObject);
begin
  FCommChannel := CreateTwoWayChannel(1024);
  FClient1              := CreateTask(TCommTester.Create(FCommChannel.Endpoint1))
                                  .MonitorWith(OmniTED)
                                  .Run;
  FClient2              := CreateTask(TCommTester.Create(FCommChannel.Endpoint2))
                                  .MonitorWith(OmniTED)
                                  .Run;
end;

In the task worker’s constructor, keep a local reference to the end point:

constructor TCommTester.Create(commEndpoint: IOmniCommunicationEndpoint);
begin
  inherited Create;
  ctComm := commEndpoint;
end;

In the task worker’s initializer, register the end point with its task:

function TCommTester.Initialize: boolean;
begin
  Task.RegisterComm(ctComm);
  Result := true;
end;

From the above, you can see, the steps are:

  1. Create the two way channel using “CreateTwoWayChannel”,  the number 1024 indicates the length of the message queue;
  2. Associate the two end points of the channel with two task workers;
  3. Inside the task worker’s Initialize( ),  register the communication end point with the worker’s IOmniTask.

After all these are done, and wen a task worker wants to talk to the other task worker, simply do something like below, then the other task worker’s MSG_FORWARDING message handler will be invoked.  Isn’t that nice?

Therefore, if a task worker wants to talk to its task control,  it can keep using Task.Comm.Send, or if it wants to talk to another task worker, use
ctComm.Send(MSG_FORWARDING,  msg.MsgData);

In case you want a task worker to send messages to ITSELF,   you can register the same communication channel “twice” with the same task worker like the following:


function TCommTester.Initialize: boolean;
begin
  Task.RegisterComm(ctComm);
  Task.RegisterComm(ctComm.OtherEndpoint);
  Result := true;
end;

Cool!

Using Delphi DLL with C++

Delphi has  good support for database applications.  Recently I have been  working on a scientific application in C++/Qt – the data access part of this application was  implemented as a Delphi DLL .

In order to use a Delphi DLL with a C++ host, there are a few options:

  1. Generate proper import lib  by defining your own def file, see this post
  2. Convert Delphi DLL to static lib using this tool
  3. Or dynamically loading the DLL at run time.  For that,  you’ll  have to do the following tedious steps(excerpted from MSDN):
// Define the function prototypes
 typedef short (CALLBACK* FindArtistType)(LPCTSTR);
// Load the DLL using the LoadLibrary function, and keep 
// the handle to the DLL instance.  
dllHandle = LoadLibrary("art.dll");
// Get a pointer to each function using the GetProcAddress 
// function. Cast function pointers to the types defined in the
// first step. 
FindArtistPtr = (FindArtistType)GetProcAddress(dllHandle,
"FindArtist");
// Verify function pointers, and use the fuctions
if (runTimeLinkSuccess = (NULL != FindArtistPtr))
   short retVal = FindArtistPtr(myArtist);
// Unload the DLL.
freeResult = FreeLibrary(dllHandle);
Qt provides a very nice class, QLibrary to streamline the above procedures, and the nice thing is, the instance is ref counted, hence the DLL won’t be unloaded unless all QLibrary instances go out of scope. The following is the sample code that loads the dll  developed in Delphi. Isn’t that nice?