![]() |
Welcome to DrDAQ Linux Programming
|
| 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. |
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 picoGUI portion.
Preparation |
There is another batch of SIMPL code which we are going to make use of as "seed code" in this lesson.
You can download and install the SIMPL examples tarball from here.
We are going to be using Tcl/Tk as our GUI language in this lesson. For those of you who have come from the iCanProgram: Linux Programming the SIMPL Way course you will already be familiar with this language and the SIMPL extensions for it.
The seed code we are going to start with is Tcl/Tk without the GUI elements:
$SIMPL_HOME/examples/tclReceiver.Once we get the SIMPL messaging working in this code we will be extending it to add in the required GUI elements to enable our LED toggle. For this we will be borrowing elements from another SIMPL example:
$SIMPL_HOME/examplex/tcletOnce again we are going to do some more time honored embrace and extend evolutionary coding to modify this Tcl/Tk seed code to meet the needs of our picoGUI.
I'm going to suggest that you create a set of subdirectories called
icanprogram/picoGUI/includeLet's go ahead and populate our new directories with the "seed code" (assuming you are at icanprogram subdirectory).
icanprogram/picoGUI/src
icanprogram/picoGUI/test
cp $SIMPL_HOME/examples/tclReceiver/include* picoGUI/include
cp $SIMPL_HOME/examples/tclReceiver/src/* picoGUI/src
cp $SIMPL_HOME/examples/tclReceiver/src/* picoGUI/test
Notice:I have asked you to replicate the tclReceiver/src contents into both the src and test subdirectories under picoGUI. This is not a typo. |
Go ahead and get this code to build (from the picoGUI/src only for now).
Follow the readme in picoGUI/test to try to operate this example code.
Because we are going to be operating a GUI ultimately we will need to
create two separate test scripts (designed to be run from two separate
console windows) to bring up all the modules and test stubs.
Let's build up our test scripts |
I'm going to suggest that we create the 2 scripts as:
The gotest script is going to handle the startup (and shutdown) of the tclStim module. The gotest script will be run on a separate console window once the gogui script has been started in the first console window.
Go ahead and verify that you can still run the tclReceiver example as before but from your two test scripts this time.
Before we go renaming our code in a more appropriate manner lets remove
the duplication in the src and test subdirectories.
Removing duplication in seed code |
In our picoGUI/src directory we will only want to retain the files with the .tcl extension and the Makefile.
Go ahead and remove all dependency on the tclStim from the picoGUI/src/Makefile.
While you are at it we should rename the INSTALL_BIN_DIR to ../bin and redo the copy line in the install rule to copy tclReceiver.tcl into a more appropriately named picoGUI.tcl.
We want to do the reverse changes in picoGUI/test. ie. only build tclStim.
Do one more build and run using the test scripts to be certain that everything is well.
We are now going to introduce our picoCourier and picoMgr messages into
this application.
Tokenized messages: picoCourierMsgs.h and picoMgrMsgs.h |
If we examine the tclRecieverMsgs.h header we can see that the example seed code already offset that token set to 0xa0.
We do see that the token size has been set to be a short (16 bit) integer. We'll need to expand our tokens to be compatible across the board at the full (32 bit) integer. More on this in a moment.
The first header we want to introduce to tclStim.c is the header containing the tokenized message definitions associated with picoCourier:
picoCourierMsgs.hFirst we need to modify the picoGUI/test/Makefile somewhat to add the search path to this picoCourier/include directory.
PICOCOURIER_INCL_DIR=../../picoCourier/include CDFLAGS=\ -c\ -g\ -Wall\ -I $(LOCAL_INCL_DIR)\ -I $(PICOCOURIER_INCL_DIR)\ -I $(SIMPL_INCL_DIR) |
Next we want to add the picoCourierMsgs.h include line to tclStim.c
#include "picoCourierMsgs.h" |
Things should recompile once again, however we haven't made use of anything in this new set of tokenized messages yet.
I'm going to suggest that we add to the tclStim capabilities by introducing a:
w - send WHAT_YA_GOT to GUIfunctionality.
Go ahead and add a new case statement modeled after the existing case
't':
case 'w': // issue WHAT_YA_GOT
{
CR_WHAT_YA_GOT_MSG *outMsg;
fcLogx(__FILE__, fn,
globalMask,
TCLSTIM_MARK,
"sending WHAT_YA_GOT -> id=%d",
toPid
);
outMsg=(CR_WHAT_YA_GOT_MSG *)outArea;
outMsg->token = CR_WHAT_YA_GOT;
Send(toPid, outArea, inArea, sizeof(CR_WHAT_YA_GOT_MSG), MAX_MSG_BUFFER_SIZE);
}
break;
|
When you compile and run this test and issue a "w" at the tclStim prompt the other console running the gogui script should report:
token=0x0 unsupportedOur next job will be to plumb the picoGUI.tcl such that this tokenized message is handled.
Handling the CR_WHAT_YOU_GOT_MSG |
Where are these tokens actually defined in our Tcl/Tk source?
globalVars.tclTcl/Tk has no concept of importing C header files. As such globalVars.tcl needs to be adjusted to reflect the numeric values of the tokens in the corresponding C file; in this case picoCourierMsgs.h.
If we look at this C file we discover that the token value associated with:
CR_WHAT_YA_GOTis zero.
As such we need to add a line to globalVars.tcl something like:
set CR_WHAT_YA_GOT 0x0
A quick examination of the .tcl files contained at picoGUI/src
shows that
msgHandlers.tclis the file which contains the error message reported above.
In fact this is the file which contains an if-else ladder to
extract the message tokens arriving at this Tcl/Tk SIMPL receiver.
Why the if-else construct?Tcl/Tk case statements are a bit more obtuse to use than their C equivalents. I've just found that if-else ladders are easier for newcomers to understand. |
What we need to do here is add another if-else branch to handle the new CR_WHAT_YA_GOT token.
I'm going to suggest that for the moment we simply log the arrival of
this token and Reply immediately, although shortly we are going
to get into some reply blocking logic.
} else {
if { $token == $CR_WHAT_YA_GOT } {
catch {puts stdout [format "token=CR_WHAT_YA_GOT(0x%X)" $token ] }
logit $logger $this $fn $MASK_MISC $logMask [format "token=CR_WHAT_YA_GOT(0x%X)" $token ]
} else {
puts stdout [format "token=0x%X unsupported" $token]
}
}
}
|
Don't forget to declare the
global CR_WHAT_YA_GOTin the declaration block of this function.
If you recall from the discussion above we have done something here which is only working by fluke. Our Tcl/Tk seed code was assuming that the token size was a short (16 bit) integer but we are actually sending a full (32 bit) integer as part of our CR_WHAT_YA_GOT message. We got away with it because our token was 0.
It is time to make the necessary repairs before we go much further.
First stop is tclReceiverMsgs.h
Let's make all the tokens in the messages equal to TCL_RECEIVER_TOKEN.
If we recompile and run our test now it should break.
To make the repairs we need to adjust the various
binary scanstatements in msgHandlers.tcl to move the token from short integer (s1) to full integer (i1).
This code should now recompile and run as before except that now the CR_WHAT_YA_GOT token is intercepted and handled (for the time being at least).
Let's now work to extend our Tcl/Tk code into a GUI format.
Adding the GUI elements |
I'd like to take advantage of another piece of seed code to help with our extension of the Tcl/Tk code into a GUI format:
$SIMPL_HOME/examples/tclet/srcIn particular I'd like to incorporate the file called:
guiHandlers.tclThe first step is easy.
Just copy this file into the picoGUI/src, modify the Makefile to add this to the build and nothing should change.
We haven't called any of the functions so nothing was broken or no behavior was adjusted.
First up we need to recognize that the tclet code is utilizing a different method of connecting to the SIMPL library. The tclet is using a TCP/IP socket and the tclSurrogate to arrange that connectivity.
In our application we don't need that level of abstraction. We are utilizing the Tcl/Tk SIMPL extensions directly.
As such some code in our newly copied guiHandlers.tcl needs to be modified.
We need to replace all instances of
vc_logit $mySocketwith
logitWe need to comment out or remove all remaining statements which reference the mySocket variable.
The Tcl/Tk interpreter which runs the GUI mode is called wish as opposed to the tclsh interpreter we have been using up until now. It should be a simple matter of replacing this in the
topPart.tclWhile we are at this file we need to import some GUI frame lines from our equivalent tclet seed code here:
set f [frame .myArea -borderwidth 5 -width 400 -height 250] set m [frame $f.main -width 400 -height 200] set b [frame .bottomBar] |
We'll need to add some more GUI code to
bottomPart.tclAgain from our tclet seed code go ahead and merge in the following lines. Take care to remove the references to myPid.
button $b.quit -text "quit" -command quitThis
pack $b.quit -side right
label $b.info -width 50 -relief groove -borderwidth 5 -justify left
pack $b.info -side left
pack $b -side bottom -fill x
renderMain
pack $f -side top -expand true -fill both
putInfo [format "myName=<%s> myslot=%s" $myName $myslot]
fileevent $recvid readable doReceive
vwait forever
name_detach
puts stdout {done}
exit
#
#================ end bottomPart =======================
|
Notice the addition of the exit statement at the bottom. This is necessary to force wish to terminate the GUI completely.
Go ahead and run this code now and the tclet GUI should render on your screen.
The "quit" button should work to exit the module.
Our next objective is to get the CR_WHAT_YA_GOT message to activate the "Reply" button on this tclet GUI.
Once we have that operational all that remains is for us to morph this
GUI such that the button name reflects our objective of toggling the LED
on the DrDAQ board.
Plumbing the PICO_LED_TOGGLE Reply from GUI |
Remember what we are after here.
We want to expose a GUI which is a SIMPL receiver. We want to send that GUI a CR_WHAT_YA_GOT message. We want the GUI to hold off replying until the user clicks on a button on the GUI screen. At that point we want to compose and Reply() a tokenized message containing the PICO_LED_TOGGLE token back to the original CR_WHAT_YA_GOT sender. This sender will then resend that reply to picoMgr which will arrange to toggle the LED on the DrDAQ board.
We'll start by making a small modification to the msgHandlers.tcl file to merge in some more changes from the tclet seed code.
First up we need to move the Reply() statements inside the individual branches of the if-else ladder. This will allow us to leave it out in the CR_WHAT_YA_GOT branch.
Once we have done that we need to modify the CR_WHAT_YA_GOT branch to
remember the sender's ID into a global variable called pendingEventFrom.
We then need to activate the tclet GUI's "reply" button by calling the
showMsg function.
} else {
if { $token == $CR_WHAT_YA_GOT } {
set pendingEventFrom $fromWhom
catch {puts stdout [format "token=CR_WHAT_YA_GOT(0x%X)" $token ] }
logit $logger $this $fn $MASK_MISC $logMask [format "token=CR_WHAT_YA_GOT(0x%X)" $token ]
showMsg "Hi"
} else {
puts stdout [format "token=0x%X unsupported" $token]
}
}
}
catch {puts stdout [format "%s:done" $fn]}
};#end hndlMsg
#
#======================== end msgHandlers =====================
#
|
Don't forget to declare:
global pendingEventFromat the top of the hndlMsg function.
We will want to initialize the value for pendingEventFrom to -1 inside globalVars.tcl.
This will get the sender's ID for the CR_WHAT_YA_GOT message token remembered.
Next up we need to compose and send out a Reply containing the PICO_LED_TOGGLE when the user hits the existing reply button.
For this we need to modify some code inside the hitMe function
contained in the guiHandlers.tcl file.
logit $logger $this $fn $MASK_MISC $logMask [format "toWhom=%s reply=<%s>" $pendingEventFrom $myreply ]
if { $pendingEventFrom != -1 } {
set reply [binary format "i1" $PICO_LED_TOGGLE]
Reply $pendingEventFrom $reply 4
logit $logger $this $fn $MASK_MISC $logMask [format "sent ACK back to %s" $pendingEventFrom ]
putInfo [format "message to %s ACK'd as <%s>" $pendingEventFrom $myreply]
set pendingEventFrom -1
$m.incoming config -text "incoming message goes here" -background red
}
$m.hitme config -state disabled
};# end hitMe
|
Once again you'll want to declare the PICO_LED_TOGGLE value inside globalVars.tcl to be:
set PICO_LED_TOGGLE 0x100and declare the global variable near the top of the hitMe function as:
global PICO_LED_TOGGLEThis code should now "work" after a fashion.
We now need to plumb the tclStim to display the PICO_LED_TOGGLE token to acertain that it is being Reply()'d correctly.
For this you will need to expand the case 'w' block somewhat
to look like:
case 'w': // issue WHAT_YA_GOT
{
CR_WHAT_YA_GOT_MSG *outMsg;
PICO_TOKEN *intoken;
fcLogx(__FILE__, fn,
globalMask,
TCLSTIM_MARK,
"sending WHAT_YA_GOT -> id=%d",
toPid
);
outMsg=(CR_WHAT_YA_GOT_MSG *)outArea;
outMsg->token = CR_WHAT_YA_GOT;
Send(toPid, outArea, inArea, sizeof(CR_WHAT_YA_GOT_MSG), MAX_MSG_BUFFER_SIZE);
intoken=(PICO_TOKEN *)inArea;
fcLogx(__FILE__, fn,
globalMask,
TCLSTIM_MARK,
"intoken=0x%X",
*intoken
);
}
break;
default:
printf("unknown command <%s>\n",p);
break;
} // end switch
|
You'll have to modify the Makefile in a similar fashion to that which was done to introduce picoCourier/include path to introduce the path to picoMgr/include.
You'll need to add the:
#include "picoMgrMsgs.h"near the top of tclStim.c
Now that we have the desired PICO_LED_TOGGLE functionality we need to
adapt a more appropriate face to our GUI, which is currently
looking a whole lot like the tclet seed code from which it was borrowed.
picoGUI cleanup |
The changes we want to make will all occur in the guiHandlers.tcl file.
Be creative.
Here is one suggestion on how to modify the renderMain function:
#=============================================
# renderMain - entry point
#=============================================
proc renderMain { } {
global m
global b_c
set fn "renderMain"
label $m.for -text "This application was created for iCanProgram DrDAQ course"
label $m.by -text "by iCanProgram Inc."
place $m.for -x 10 -y 50
place $m.by -x 10 -y 80
button $m.hitme -text "LED Toggle" -command hitMe -state disabled
place $m.hitme -x 170 -y 165
pack $m -fill both -expand true
putInfo [format "%s: done" $fn]
} ;# end renderMain
|
Likewise for the the hitMe button handler function:
#=========================================
# hitMe - entry point
#=========================================
proc hitMe { } {
global MY_TEST
global m
global logger
global this
global MASK_MISC
global logMask
global pendingEventFrom
global PICO_LED_TOGGLE
set fn "hitMe"
catch {puts stdout [format "%s: ding" $fn]}
logit $logger $this $fn $MASK_MISC $logMask [format "toWhom=%s" $pendingEventFrom ]
if { $pendingEventFrom != -1 } {
set reply [binary format "i1" $PICO_LED_TOGGLE]
Reply $pendingEventFrom $reply 4
logit $logger $this $fn $MASK_MISC $logMask [format "sent PICO_LED_TOGGLE back to %s" $pendingEventFrom ]
putInfo [format "PICO_LED_TOGGLE to %s" $pendingEventFrom ]
set pendingEventFrom -1
}
$m.hitme config -state disabled
};# end hitMe
|
Once you have this code operating in the unit test go ahead and integrate your whole application together.
You should be able to create a master startup script which will startup (in this order)
Wow !
![]() |
Congratulations! You have successfully built and tested the most basic application we set out to do for this course. |
Exercises |
The DrDAQ unit is capable of much more than the toggling of an LED.
For most of the other functions you will need to transport data back to the GUI from the DrDAQ unit itself.
This is not very difficult to do.
I would suggest that you practice first by expanding the PICO_LED_TOGGLE_MSG to include the ledState as data.
It would work something like this:
Summary |
Let's summarize what you have learned in Lesson4