Welcome to DrDAQ Linux Programming
Lesson #3: picoCourier

Course content Enter chat room Send email 
to mailing list
Check calendar

JUST A REMINDER:  for those of you who have not yet received your confirmation that you have been
subscribed to the mailing list please contact us by email immediately.

In memory of Linda.  Worldwide Cancer sites here.    Canadian Cancer donations here.
 
 

Hint:

All of these lessons  are designed to be worked on offline.  For those of you with dialup Internet access  you might find it helpful to print off the page for each lesson and work from the printout. 

By now you should have all the tools installed and operational.      You should have a working driver and picoMgr SIMPL interface to that driver ... at least for the purposes of toggling the LED on the DrDAQ board.

If you recall from lesson #1 our picture of the modules we are trying to build:

Continuing with our right to left movement, in this lesson we are going to be developing the picoCourier portion.
 
 

Why do we need the picoCourier anyway?

What exactly is a courier softwareIC?

A courier softwareIC is a SIMPL construct which is used to relay messages between 2 SIMPL receivers.

Why do we need a picoCourier anyway?

To understand this question we need to step back into the GUI process itself.

We need to examine the GUI in terms of the SIMPL "receiver" or "sender" paradigm.

It is not too hard to recognize that a GUI is already multiplexing on inputs (mouse, keyboard).     GUI's are setup to be natural "receivers".   If we go ahead and introduce another input/output scheme to our GUI (SIMPL messaging) we will want that to smoothly integrate with the existing GUI functionality.      In other words a GUI which contains a blocking SIMPL sender logic will not necessarily operate well.

If we examine the SIMPL message passing in detail we find that both the Send() and the Reply() can carry data.

The Reply() branch is somewhat like a non blocking "send".      We want to make use of this non blocking Reply() data path for our GUI.

The softwareIC we want to take advantage of to convert a  Reply() from the GUI into a Send() into the picoMgr is the courier.

It will work as follows.     The picoCourier will come up and Send() a WHAT_YA_GOT message to the GUI.    The GUI will simply not Reply() and hold the picoCourier send blocked.     At some point the GUI user will click on a widget in the interface which requires interaction with the DrDAQ (eg.  toggle LED button).     A Reply() will be composed and issued to the picoCourier.    Upon receiving this Reply() the picoCourier will simply Send() it on to the picoMgr.     The picoMgr will get the message, do its thing with the DrDAQ and Reply() back to the picoCourier.     To complete the sequence the picoCourier will convert the picoMgr reply message into a Send() to the GUI.

In some fashion the picoCourier is a Reply() <-> Send() converter.

The messaging we are going to build will look something like the following:


GUI                        picoCourier                   picoMgr


                  <-S     CR_WHAT_YOU_GOT
PICO_LED_TOGGLE   Y->     PICO_LED_TOGGLE        S->
                  <-S     CR_WHAT_YOU_GOT        <-Y    null

 

Preparation

When you downloaded and installed the SIMPL softwareICs tarball  as part of the previous lesson some very capable courier "seed code" was installed on your system.

We are going to do some more time honored embrace and extend evolutionary coding to modify this courier seed code to meet the needs of our picoCourier.

I'm going to suggest that you create a set of subdirectories called

icanprogram/picoCourier/include
icanprogram/picoCourier/src
icanprogram/picoCourier/test
Let's go ahead and populate our new directories with the "seed code" (assuming you are at icanprogram subdirectory).
cp $SIMPL_HOME/softwareICs/courier/*  picoCourier
cp $SIMPL_HOME/softwareICs/courier/include/*  picoCourier/include
cp $SIMPL_HOME/softwareICs/courier/src/*  picoCourier/src
cp $SIMPL_HOME/softwareICs/courier/test/*  picoCourier/test
Go ahead and make sure that this code builds in its new home.
 
 
 

Reminder:

In the previous lesson we introduced you to the change -> recompile -> test -> change ...  cycle of software development.      Your test script here is going to be the picoCourier/test/gocourier script.

 

Let's build up our test stubs associated with picoCourier

From the picture above you can see that picoCourier will be interfacing with both the GUI and the picoMgr.

If we examine the test stubs that are currently in our "seed code" at picoCourier/test/  we have

guiSim
receiverSim
The guiSim is a good name.

Let's rename receiverSim as a more appropriate picoMgrSim.

mv receiverSim.c  picoMgrSim.c
mv recvSimInit.c  picoMgrSimInit.c
mv recvSimUtils.c  picoMgrSimUtils.c
While we are at it let's rename some header files as well at picoCourier/include/:
mv receiverSim.h  picoMgrSim.h
mv recvSimProto.h  picoMgrSimProto.h
Go ahead and make the appropriate replacements for receiverSim.h and recvSimProto.h in the source tree and get a clean recompile.
 
 

Tokenized messages: picoMgrMsgs.h

If we examine the courierMsgs.h header in our picoCourier/include/  we can readily identify a set of tokens associated with messages the courier will be exchanging with other processes.

If we then reexamine the picoMgr/include/picoMgrMsgs.h header that we constructed in the last lesson we see some overlapping token values associated with messages that the picoMgr will be receiving.

How are we going to rationalize this?

Fortunately because of our tokenized scheme the solution is relatively painless.      Let's offset the tokens in picoMgrMsgs.h by some amount eg. 0x100.
 
typedef enum
        {
        PICO_LED_TOGGLE=0x100,
        MAX_NUM_PICO_TOKENS
        }PICO_TOKEN;

Go ahead and recompile your picoMgr code from lesson #2.

While we are at this change it would be better to rationalize the token size between our 3 communicating processes.

Currently the courier seed code is using a 16 bit token (UINT16) while the picoMgr is using an full integer sized token (32 bits on a normal PC).

To make this change you'll need to modify your picoCourierMsgs.h header contents to look something like:
 
typedef enum
        {
        CR_WHAT_YA_GOT,
        CR_TEST,
        CR_TEST_REPLY,
        CR_ERROR,
        MAX_COURIER_TOKENS
        }COURIER_TOKEN;

typedef struct
        {
        COURIER_TOKEN token;   //CR_WHAT_YA_GOT
        }CR_WHAT_YA_GOT_MSG;

typedef struct
        {
        COURIER_TOKEN token;   //CR_TEST, CR_TEST_REPLY
        char toWhom[20]; // SIMPL name
        char str[80];
        }CR_TEST_MSG;

typedef struct
        {
        COURIER_TOKEN token;   //CR_ERROR
        }CR_ERROR_MSG;

 
 

 picoMgrSim

So far all we've done is to make preparatory changes to our picoCourier code base.

There is a test script that we now want to modify in keeping with all our changes

picoCourier/test/gocourier
Once you have that operational let's make the changes required to allow picoMgrSim to respond to the PICO_LED_TOGGLE.

First up we need to allow our picoCourier to gain access to the picoMgrMsgs.h tokens and messages.

We need to modify the picoCourier/test/Makefile to add the picoMgr/include directory to the header search path:
 
LOCAL_INCL_DIR=../include
SIMPL_INCL_DIR=$(SIMPL_DIR)/include
PICOMGR_INCL_DIR=../../picoMgr/include



CDFLAGS=\
        -c\
        -g\
        -Wall\
        -I $(LOCAL_INCL_DIR)\
        -I $(SIMPL_INCL_DIR)\
        -I $(PICOMGR_INCL_DIR)

Then we need to modify each of the following files

picoCourier/test/picoMgrSim.c
picoCourier/test/picoMgrSimInit.c
picoCourier/test/picoMgrSimUtils.c
to insert picoMgrMsgs.h below the courierMsgs.h:
 
#include "courierMsgs.h"
#include "picoMgrMsgs.h"

 

Hint:

Don't forget the change -> recompile -> retest -> change ...  cycle that we illustrated in lesson #2.

Finally we are going to change the case statement in picoMgrSim.c to add in the PICO_LED_TOGGLE token handler.    Essentially we'll simply want to record the fact that this token was received into the trace log.

NOTE: that we are also changing the token from a UINT16 to a straight int value.
 
 
        else
// Is this from receive fifo
        if(FD_ISSET(my_fds[0], &inset))  //  receive is ready
                {
                int nbytes;
                char *sender;
                int *token;

                token=(int *)inArea;

                nbytes = Receive(&sender, inArea, MAX_MSG_SIZE);

                switch(*token)
                        {
                        case PICO_LED_TOGGLE:
                                fcLogx(__FILE__, fn,
                                        globalMask,
                                        RECV_SIM_MISC,
                                        "PICO_LED_TOGGLE");

                                Reply(sender, NULL, 0);
                                break;

                        case CR_TEST

 
 

 guiSim

I am trying to illustrate some tried and true programming habits.

One of those is to build up the test stubs and the test strategies before coding actually begins.      As we indicated previously this is based on simple cause and effect to promote rapid convergence of complex code bit by bit.

First up we need to add the

#include "picoMgrMsgs.h"
to the guiSim source files.

We now need to teach our guiSim to Reply() our PICO_LED_TOGGLE tokenized message.
 
 
// Is this from keyboard
        if(FD_ISSET(my_fds[0], &inset))  //  keyboard is ready
                {
                fgets(line, 79, stdin);

                switch(line[0])
                        {
                        case '?': // help
                                printf("guiSim commands:\n");
                                printf("t <str> - courier test string\n");
                                printf("L  - toggle the LED\n");
                                printf("q - quit\n");

                                printf("-> ");
                                fflush(stdout);
                                break;

                        case 'L': //string
                                {
                                PICO_LED_TOGGLE_MSG *outMsg;

                                if(courierPending != NULL)
                                        {
                                        fcLogx(__FILE__, fn,
                                                globalMask,
                                                GUI_SIM_MISC,
                                                "PICO_LED_TOGGLE");

                                        outMsg=(PICO_LED_TOGGLE_MSG *)outArea;
                                        outMsg->token=PICO_LED_TOGGLE;

                                        fcLogx(__FILE__, fn,
                                                globalMask,
                                                GUI_SIM_MISC,
                                                "sending LED_TOGGLE to picoMgr via courier",
                                                );

                                        Reply(courierPending,
                                                outArea, 
                                                sizeof(PICO_LED_TOGGLE_MSG));

                                        courierPending = NULL;
                                        }
                                else
                                        {
                                        printf("courier busy\n");
                                        printf("-> ");
                                        fflush(stdout);
                                        }
                                }
                                break;

                        case 't': //string

When you recompile and retest you will now find that the picoCourier is rejecting the PICO_LED_TOGGLE token as unknown.

Now we can procede to provide that functionality into the picoCourier itself.
 
 

 picoCourier

The first stop will be to add the

#include "picoMgrMsgs.h"
to the
picoCourier/src/picoCourier.c
picoCourier/src/picoCourierInit.c
files in a similar manner to how we made the changes for the test stubs.

We'll also have to make a change to the Makefile in a similar manner to how we changed the picoCourier/test/Makefile to add the include path to picoMgr/include.

If we examine the way the picoCourier seed code has been structured we find that the incoming messages contain the SIMPL name of the receiver towhom the message is to be couriered.      We don't need nor want that flexibility here.    Instead we want to pass the picoMgr SIMPL name directly to the courier on startup via a command line parameter.

First stop in that process is for us to add a picoMgrID global variable to picoCourier.h as:
 
_ALLOC int picoMgrID; 

Next we need to add the explicit name_locate() call into the picoCourierInit.c in exactly a similar manner to the case for the GUI name_locate():

NOTE: you will have to declare a local variable called picoMgrName.
 
                        case 'p':
                                {
                                int j;

                                if(*++p == 0) p++;
                                sprintf(picoMgrName,"%.19s",p);

                                for(j=0; j<2; j++)  // try 3 times
                                        {
                                        picoMgrID=name_locate(picoMgrName);
                                        if(picoMgrID != -1) break;
                                        printf("retrying nameLocate after 1sec sleep\n");
                                        sleep(1);
                                        }

                                if(picoMgrID == -1)
                                        {
                                        printf("%s: can't locate %s\n",
                                                fn,picoMgrName);
                                        exit(0);
                                        }
                                }
                                break;

Remember as you do your test cycle to edit the gocourier script to add the "-p" parameter to the picoCourier invocation line.

Next stop

picoCourier/src/picoCourier.c
We are going to want to add a case statement into the main switch for the PICO_LED_TOGGLE and direct the message off to the picoMgrID which we just determined via our name_locate() call.

NOTE:  you are going to want to recast the token as an int pointer here as well.
 
token=(int *)inArea;

outMsg=(CR_WHAT_YA_GOT_MSG *)outArea;
outMsg->token = CR_WHAT_YA_GOT;

while(!x_it)
        {
        Send(guiID, outArea, inArea, sizeof(CR_WHAT_YA_GOT_MSG), MAX_MSG_SIZE);

        switch(*token)
                {
                case PICO_LED_TOGGLE:
                        {
                        fcLogx(__FILE__, fn,
                                globalMask,
                                COURIER_MISC,
                                "LED_TOGGLE courier'd to picoMgr"
                                );

                        Send(picoMgrID, 
                                inArea, 
                                inArea, 
                                sizeof(PICO_LED_TOGGLE_MSG), 
                                MAX_MSG_SIZE);

                        }
                        break;

                case CR_TEST:

Wow !

Now that was fiddly.
 
 
Congratulations!  You have successfully built and tested the glue portion between your yet to be built GUI code and the previously built picoMgr code!

 
 

Exercises

In the next lesson we will be building the Tcl/Tk GUI with enough smarts to toggle the LED.

Go ahead and develop the skeleton of this process.

You should be able to integrate the real picoMgr into a clone of the gocourier script and have our guiSim command actually toggle the LED.    Go ahead and try this on your own.
 
 

Summary

Let's summarize what you have learned in Lesson3

End of Lesson 3.


Copyright of iCanProgram Inc.  2003