Welcome to SIMPL Programming for Python
Lesson #6

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

Lesson Overview

In this lesson we want to think about how we would transport messages between a python gui-based sending program running on a Windows host and a C coded receiving program running on a Linux host.

We have purposefully chosen a situation where Windows/Linux communications are necessary. The situation could be Linux/Linux or Linux/MAC OS X or Windows/MAC OS X or Unix/Linux etc. The Windows/Linux scenario has occurred in a real-world project we were involved in. We believe that it is important to be able SIMPL communicate with programs running on a Windows platform due to the shear number of Windows hosts in existence. It would be a shame if a project specified that the user interface resides on a Windows host and there was no easy way to interface this requirement to the rest of the system.

SIMPL communications between Windows and Linux hosts is accomplished with the aid of the tclSurrogate. This program is named this way because its original purpose was to allow a Tcl-based program running on a Windows host to SIMPL communicate through a network with any SIMPL program on a Linux host.

In this lesson we are going to use the tclSurrogate to allow a python program to SIMPL communicate with a C program; this is exactly the same situation as in Lesson #4. This requires the use of a new module called pysimpl.

We recommend that you read the documents called readme.tcl and readme.python which are located in the docs directory below the SIMPL root directory. These docs contain many details regarding the tclSurrogate program and the pysimpl python module.

It must be made clear at this point however, SIMPL communications between networked hosts is normally accomplished with the aid of the TCP/IP or RS-232 surrogates. For example, in most cases SIMPL communications between two Linux hosts would be via TCP/IP surrogates. The tclSurrogate is normally used for Tcl or Python programs only running on Windows. For more information on the other surrogates see readme.surrogateTCP also in the docs directory.

As in Lesson #4, the sender program is able to send three different types of messages and the receiver program to receive these messages and react appropriately.


Note:

For the purposes of this lesson, we are going to run all of our programs on one Linux host. We will replace the network aspect of our real world example by routing our SIMPL communications through the TCP/IP loopback interface.

With this algorithm, we hope to demonstrate the following:

  1. how a gui-based python program which could be located on a Windows host can be made to SIMPL communicate with a C program located on a separate Linux host.

Note:

Do NOT cut and paste the following code pieces. The scripts are available in the .../src directory for you to run directly.

Let's examine the sending script.

The following code can be found in the ../src/senderw_gui.py file. We will examine it line by line.
 
1. """
2. senderw_gui.py: This is a python simpl gui-based sender.
3. Refer to lesson #6.
4. Usage: python senderw_gui.py
5. """
6.
7. #! /usr/bin/python
8.
9. # import necessary modules
10. import sys
11. import wssimpl
12. from Tkinter import *
13.
14. nee = None
15.
16. # define the callback for button 1
17. def hndlButton1(event):
18.     token = 10
19.     nee.packInt(token, nee.BIN)
20.     a = 99
21.     nee.packInt(a, nee.BIN)
22.     b = 999
23.     nee.packInt(b, nee.BIN)
24.     c = 9999
25.     nee.packInt(c, nee.BIN)
26.     makeSend()
27.
28. # define the callback for button 2
29. def hndlButton2(event):
30.     token = 20
31.     nee.packInt(token, nee.BIN)
32.     d = 3.1415
33.     nee.packFloat(d, nee.BIN)
34.     makeSend()
35.
36. # define the callback for button 3
37. def hndlButton3(event):
38.     token = 30
39.     nee.packInt(token, nee.BIN)
40.     s = "We are the knights who say Neee."
41.     l = len(s)
42.     nee.packInt(l, nee.BIN)
43.     nee.packString(s, nee.BIN)
44.     makeSend()
45.
46. # define the call for the simpl send
47. def makeSend():
48.     retVal = nee.send(receiverId)
49.     if retVal == -1:
50.         print "%s: send error-%s" %(sName, nee.whatsMyError())
51.         sys.exit(-1)
52.
53. # define the exit call
54. def die(event):
55.     sys.exit(0)
56.
57. # initialize some required variables
58. sName = "50000:localhost:SENDER"
59. rName = "RECEIVER"
60.
61. # create an instance of Simpl
62. nee = wssimpl.Simpl()
63.
64. # attach a simpl name
65. retVal = nee.nameAttach(sName, 1024)
66. if retVal == -1:
67.     print "%s: name attach error-%s" %(sName, nee.whatsMyError())
68.     sys.exit(-1)
69.
70. # name locate the receiver
71. receiverId = nee.nameLocate(rName)
72. if receiverId == -1:
73.     print "%s: name locate error-%s" %(sName, nee.whatsMyError())
74.     sys.exit(-1)
75.
76. # initialize Tk for graphics
77. root = Tk()
78.
79. # set up a button to initiate the sending of message token type 10
80. button = Button(root)
81. button["text"] = "send message token type 10"
82. button.bind("", hndlButton1)
83. button.pack()
84.
85. # set up a button to initiate the sending of message token type 20
86. button = Button(root)
87. button["text"] = "send message token type 20"
88. button.bind("", hndlButton2)
89. button.pack()
90.
91. # set up a button to initiate the sending of message token type 30
92. button = Button(root)
93. button["text"] = "send message token type 30"
94. button.bind("", hndlButton3)
95. button.pack()
96.
97. # set up a button for program exit
98. button = Button(root)
99. button["text"] = "Exit"
100. button.bind("", die)
101. button.pack()
102.
103. # handle user input
104. root.mainloop()

Lines 10-12 import required modules. Note that we are using the wssimpl module because we need the tclSurrogate as our network surrogate.
Lines 17-55 define the various callback functions etc.
Notice that the various message components are set here, the binary messages are constructed and then the message is sent. We use binary type messages because we are sending to a C program which works well well with that type.
Line 48 is where the actual SIMPL send operation is called.
Line 58 sets the SIMPL name for this program. Note the format of the name, 50000:localhost:SENDER. The "50000" refers to the port number where TCP/IP sockets are bound to. We have chosen port 50000 because it should be out of the range of any port numbers usually claimed by other programs. The "localhost" refers to the host machine where the receiver program is running which is the the same as this program; normally this would not be the case. "SENDER" is our SIMPL name for this program.
Line 59 sets the SIMPL name for the receiver program.
Line 62 creates an instance of Simpl with the global name "nee".
Lines 65-68 perform the SIMPL name attach.
Lines 72-74 perform the SIMPL name locate.
Lines 77-101 create the various graphical bits required by the program. When any of the available GUI buttons are selected, a particular tokenized message is created and then sent to the SIMPL receiver program.
Line 104 creates an infinite event loop on the root window that awaits incoming events (in this case mouse clicks on the various buttons).

Let's examine the receiving program. Note: this is exactly the same receiving program used in Lessons #3 and #4.

1. /*
2. FILE: receiver.c
3. DESCRIPTION: This is a C simpl receiver.
4. Refer to lessons 3, 4, 6, 7.
5. USAGE: receiver
6. */
7.
8. // include required headers
9. #include <stdio.h>
10. #include <stdlib.h>
11. #include <simpl.h>
12.
13. // define possible message structures
14. typedef struct
15.     {
16.     int token;
17.     int var1;
18.     int var2;
19.     int var3;
20.     } MSG1;
21.
22. typedef struct
23.     {
24.     int token;
25.     float var1;
26.     } MSG2;
27.
28. typedef struct
29.     {
30.     int token;
31.     int var1;
32.     char var2[34];
33.     } MSG3;
34.
35. int main()
36. {
37. char *me = "RECEIVER";
38. char *sender;
39. char mem[1024];
40.  int n;
41. MSG1 *in1;
42. MSG2 *in2;
43. MSG3 *in3;
44. int *token;
45.
46. // perform simpl name attach
47. if (name_attach(me, NULL) == -1)
48.     {
49.     printf("%s: cannot attach name-%s\n", me whatsMyError());
50.     exit(-1);
51.     }
52.
53. while (1)
54.     {
55.     // receive incoming messages
56.     n = Receive(&sender, mem, 1024);
57.     if (n == -1)
58.         {
59.         printf("%s: Receive error-%s\n", me, whatsMyError());
60.         continue;
61.         }
62.
63.     // set a pointer to the value of the message token
64.     token = (int *)mem;
65.
66.     // decide course of action based on the value of the token
67.     switch (*token)
68.         {
69.         case 10:
70.             in1 = (MSG1 *)mem;
71.             Printf("token=%d var1=%d var2=%d var3=%d\n",
72.            & nbsp;    in1->token,
73.            & nbsp;    in1->var1,
74.            & nbsp;    in1->var2,
75.            & nbsp;    in1->var3);
76.             break;
77.
78.         case 20:
79.             in2 = (MSG2 *)mem;
80.             printf("token=%d var1=%d\n",
81.            & nbsp;    in2->token,
82.            & nbsp;    in2->var1);
83.             break;
84.
85.         case 30:
86.             in3 = (MSG3 *)mem;
87.             printf("token=%d var1=%d var2=%.*s\n",
88.                  in3->token,
89.            & nbsp;    in3->var1,
90.            & nbsp;    in3->var1,
91.            & nbsp;    in3->var2);
92.             break;
93.
94.         default:
95.             printf("%s: unknown message token=%d\n", me. token);
96.         }
97.
98.     // reply to sender
99.     Reply(sender, NULL, 0);
100.   }
101.
102. return(0);
103. }

Lines 9-11include the required C header files. Note the inclusion of the simpl.h header file.

Lines 14-20
define the binary message that corresponds to token value 10.

Lines 22-26
define the binary message that corresponds to token value 20.

Lines 28-33
define the binary message that corresponds to token value 30.

Line 35
is the start of the program.

Lines 37-44
declare necessary local variables.

Lines 47-51
handle the simpl name attach.

Line 53
starts an infinite loop which awaits incoming messages.

Lines 56-61
receive the incoming messages.

Line 64
sets a pointer to the incoming message token.

Lines 67-96
deal with each message type based on the token by printing the various message components to the screen.
Line 99 replies a null message to the blocked sender. This reply message could be anything at all but in the interests of simplicity, the reply message has been made null.

 
 

The Makefile for the C receiver program.

The receiver program must be compiled and linked before it can be of any use. Go to ../src and at the command prompt type:

make clean
Then type:
make install
This makes a new executable of receiver.c called receiver and puts it in ../bin .
 
 

Note:

In order to run this Makefile, the shell variable SIMPL_HOME must be set so that the make can find the SIMPL header files and library.

 
 

Let's run the required three programs.

In order to run the senderw_gui.py script, C receiver program, and the tclSurrogate program do the following:

  1. make sure that PYTHONPATH has been exported to your shell.
    ie. export PYTHONPATH=$(SIMPL_HOME)/python/modules
  2. open a shell window. This can be opened from a desktop such as KDE or GNOME.
  3. in this shell window,  change directory to ../bin.
  4. start the tclSurrogate program in the background. That is at the command line prompt type:
    $SIMPL_HOME/bin/tclSurrogate -n SURRO -p 50000 &
  5. start the C receiver program. That is at the command line prompt type:  receiver
  6. open another shell window next to the first one.
  7. change directory to ../src.
  8. this time, run the sender program. At the command prompt type:
    python senderw_gui.py


You should see the receiver program print the contents of each message as you choose the various message buttons displayed by the sender gui.

Let's rerun with the C receiver in the cloud

Recall that in previous lessons we had arranged to run the C receiver on the Linode at icanprogram.ca. The output from the C receiver was piped into a filter process which in turn wrapped that output in HTML for display at:

http:/icanprogram.ca/course/creceiver.html

We can now vector our sender to this cloud based receiver by changing line 58 in senderw_gui.py to something like below:


57. # initialize some required variables
58. sName = "8000:icanprogram.ca:SENDER"
59. rName = "RECEIVER"

and rerunning senderw_gui.py exactly as before.

Notice what happened here:

The very same cloud based instance C receiver that we accessed via the TCP/IP SIMPL surrogates in previous lessons has now been accessed remotely (possibly from a non Linux OS) via the tclSurrogate protocol.

 
 

Summary

Let's summarize what you should have observed in this lesson:

End of Lesson 6.


Copyright of iCanProgram Inc.  2009-2011