Welcome to SIMPL Programming for Python
Lesson #3

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

Lesson Overview

In this lesson we are going to examine the detailed workings of a python text-based sending  program and a C coded receiving program.

The sender program is able to send three different types of messages and the receiver program to receive these messages and react appropriately.

In fact, it is exactly the same sending script that was used in lesson #2. The receiver program prints to screen the various components of the messages received.

With this algorithm, we hope to demonstrate the following:

  1. how the binary message structure allows a python program with very different variable types to communicate with a C program.

 

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. Note: this is exactly the same sending program as used in Lesson #2.

The following code can be found in the ../src/sender_txt3.py file. We will examine the important points by line number.
 
      1 """
      2 sender_txt.py: This is a python simpl text-based sender.
      3 Refer to lessons #2 and #3.
      4 Usage: python sender_txt.py tokenType
      5 """
      6 
      7 
      8 # import required modules
      9 import sys
     10 import csimpl
     11 
     12 # initialize necessary variables
     13 sName = "SENDER"
     14 rName = "RECEIVER"
     15 
     16 # create an instance of Simpl
     17 nee = csimpl.Simpl(sName, 1024)
     18 if (nee == -1):
     19         print ("name attach error-", whatsMyError())
     20         sys.exit(-1)
     21 
     22 # name locate the receiver program
     23 receiverId = nee.nameLocate(rName)
     24 if receiverId == -1:
     25         print ("name locate error-",  nee.whatsMyError())
     26         sys.exit(-1)
     27 
     28 # compose the message you wish to send based on the value of the token 
     29 if sys.argv[1] == "10":
     30         print ("token = 10") 
     31         nee.packMsg(nee.BIN, "iiii", 10,99,999,9999)
     32 
     33 elif sys.argv[1] == "20":
     34         print ("token = 20")
     35         nee.packMsg(nee.BIN, "if", 20,3.1415)
     36 elif sys.argv[1] == "30":
     37         print ("token = 30")
     38         s = "We are the knights who say Neee."
     39         l = len(s)
     40         print ("str=",s)
     41         nee.packMsg(nee.BIN, "iis", 30,l,s)
     42 else:
     43         print ("unknown message type")
     44         sys.exit(-1)
     45 
     46 # send the message
     47 retVal = nee.send(receiverId)
     48 if retVal == -1:
     49         print ("send error-",  nee.whatsMyError())
     50         sys.exit(-1)

Line 10 imports the csimpl module. This module is responsible for supplying the SIMPL functionality to the Python script.
Lines 13 and 14 set the unique SIMPL names for the sender and the receiver programs. These names could be any type of string which is convenient. In this case the names are in uppercase implying that they are constants.
Line 17 creates an instance of Simpl. Like the tkinter module for example, the csimpl module API allows the programmer to call the various aspects of wcsimpl via a class instance or as module functions, depending on the application's needs and/or programming tastes.
Lines 17-20 make the necessary nameAttach() call that must precede all other SIMPL calls because nameAttach() is responsible for much of the required initialization required by SIMPL. This program is a SIMPL sender so it attaches the name created for the sender above. The parameter 1024 tells SIMPL that the largest expected incoming or outgoing message will not be larger than 1 kbyte. The call to whatsMyError() returns a string describing the SIMPL error, if encountered.
Lines 23-26 make a SIMPL nameLocate() call that returns the unique SIMPL ID of the receiving program that a message will be sent to. This is required for the SIMPL send() call to follow.
Lines 28-41 compose the message that will be sent to the receiver. Notice that all of the pack() methods contain an argument called BIN. This instructs the pack() software how to organize the data elements within the message. The possibilities are BIN or CHR. BIN instructs the packing/unpacking routines to write/read message items to/from the message memory with a binary approach to the memory; like a C binary structure. BIN would be used when sending to or receiving from a C program that uses structures as their message definitions. BIN based messages are usually larger in size because they must give deference to memory boundaries. CHR tells the packing/unpacking routines to simply read/write data items byte by byte. CHR is used when BIN is unnecessary.
Lines 29-31take the value of the token passed into the script on the command line as 10. This number serves as a "token" for the message. A token is a unique identifier which allows the receiving program to understand the message data to follow within a received message. In this case, the sender packs three integers, namely 99, 999, and 9999. When the receiver unpacks the message it is understood that the first thing in the message is an integer token which will direct any further unpacking. Hence, in this case a token value of 10 indicates that three more integers are to follow in the message content.
Lines 33-35take a token value of 20 and build a message containing a floating point number. Note that in C, floats are four byte signed numbers, while in Python all floats are C doubles (eight byte signed numbers). If all of the SIMPL programs are written in one language (Python say) and reside on similar platforms (32-bit say), then less attention needs to be paid in packing/unpacking messages. On the other hand, if the languages/platforms used view data types differently, care must be taken that the appropriate types are used when composing messages in order to preserve the proposed meaning of the message content.
Lines 36-41 use a token value of 30 and create a message containing a string. Notice that the length of the string is also sent as part of the message so that the receiver knows how long the string actually is.
Lines 47-50 perform the actual SIMPL sending of the message to the receiving program. There are no unpack() calls after the send() indicating that there is no expected reply in this case. Often, there is a reply of some sort from the receiver, but for the sake of simplicity in this example, a reply message has been omitted.

 

Let's examine the receiving program.

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.                 in1->token,
73.                 in1->var1,
74.                 in1->var2,
75.                 in1->var3);
76.             break;
77.
78.         case 20:
79.             in2 = (MSG2 *)mem;
80.             printf("token=%d var1=%d\n",
81.                 in2->token,
82.                 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.                 in3->var1,
90.                 in3->var1,
91.                 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 and library.

 
 

Let's run the two programs.

In order to run the sender_txt.py script and the C receiver 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 C receiver program. That is at the command line prompt type:  receiver
  5. open another shell window next to the first one.
  6. change directory to ../src.
  7. this time, run the sender program. That is,  at the command prompt type: python3 sender_txt3.py  10


This last command indicates:

  1. the python interpreter is to run this script,
  2. the script to run is sender_txt3.py, and
  3. the message to send is the one identified by token = 10.


You should see the receiver program report the contents of the message identified by token=10 printed in the first shell window.

Repeat step 7 but using  the token numbers 20 and 30. Again, the contents of these messages should be printed out on the shell window by the receiver program.

Let's rerun with the C receiver in the Linode cloud.

To enable message transport across a TCP/IP link to the cloud, SIMPL uses the concept of surrogates. The SIMPL surrogate daemons (parent processes) must be run on each end of the TCP/IP link. When a remote name_locate() call is requested by specifying the cloud URL as part of the composite SIMPL name (eg. icanprogram.ca:RECEIVER), two generic child surrogate processes are spawned at each end of the TCP/IP link as in the diagram above.

To enable remote name_locate logic from your end you need to run the following SIMPL daemons in the background first:

protocolRouter &
surrogateTcp &

The C receiver running on the Linode is identical to the code listed here. However in order to make the output visible via a browser the C receiver has been piped to a filter program which in turn wraps the output in HTML for perusal at:

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

To vector the message to the cloud, the composite SIMPL name will need to be integrated into line 15 of sender_txt3.py. This change points the message at the Linode:

13. # initialize necessary variables
13. sName = "SENDER"
14. rName = "icanprogram.ca:RECEIVER"

It is then a simple matter of rerunning sender_txt3.py exactly as before.

Notice what happened here:

The cloud based instance C receiver which has identical code to the instance we accessed locally has now been accessed remotely. The only change in the sender was the addition of the cloud URL (icanprogram.ca) in the SIMPL name for the receiver.

 
 

Summary

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

End of Lesson 3.


Copyright of iCanProgram Inc.  2012