Modifying The Simple Echo Client
Being able to set a component's parameters using command-line options allows the user to change a component's behavior.
To demonstrate, we will use the existing simple echo client. The code will be modified to allow the user to do the following via the command line:
- Specify the Backplane IP address
- Specify the publisher and subscriber port numbers
- Specify the number of messages to send to the server
- Specify a process name to display on the banner
- Specify the loop time
This example allows the user to specify, none, one, or any combination of parameters, allowing maximum flexibility.
To process user-specified command-line parameters, we will use the argparse library.
A code comparison will be provided for the modified code. Any new code that is required is also shown for the feature.
Once we modify the code, we will be able to print a help screen to the console by merely invoking the program with a -h command-line option. A list of all command-line options will be displayed for the user to choose from.
Adding the Necessary Imports
We will import the argparse package as well as the signal package to the original file. Argparse allows us to create the command line arguments. The signal enables us to trap and process a Control-C entered by the user.
22 import argparse
23 import signal
Modifying The Component Class
Line 29 declares the name for our modified client.
Lines 31 through 56 provide usage information.
Lines 59 defines the __init__ method, and it accepts a dictionary of parameters, called kwargs. The kwargs dictionary is populated near the end of the program.
Lines 88 through 92 dereference the kwargs values and passes the values to the parent class.
The rest of the class definition is unchanged.
29 class EchoCmdClient(BanyanBase):
30 """
31 This is an echo client that will allow the user
32 to specify command line arguments to change the default behavior
33 of the client.
34
35 It sends out a series of messages and expects an
36 echo reply from the server. When it completes, press enter, and it
37 will send a message to the server so that it also quits
38
39 To use: 1. Start the backplane.
40 2. Start the server.
41 3. Start this client.
42
43 usage: echo_cmdline_client.py [-h] [-b BACK_PLANE_IP_ADDRESS]
44 [-m NUMBER_OF_MESSAGES] [-n PROCESS_NAME]
45 [-p PUBLISHER_PORT] [-s SUBSCRIBER_PORT] [-t LOOP_TIME]
46
47 optional arguments:
48 -h, --help show this help message and exit
49 -b BACK_PLANE_IP_ADDRESS
50 None or IP address used by Back Plane
51 -m NUMBER_OF_MESSAGES
52 Number of messages to publish
53 -n PROCESS_NAME Set process name in banner
54 -p PUBLISHER_PORT Publisher IP port
55 -s SUBSCRIBER_PORT Subscriber IP port
56 -t LOOP_TIME Event Loop Timer in seconds
57 """
58
59 def __init__(self, **kwargs):
60
61 """
62 kwargs is a dictionary that will contain the following keys:
63
64 :param back_plane_ip_address: banyan_base back_planeIP Address -
65 if not specified, it will be set to the
66 local computer
67 :param subscriber_port: banyan_base back plane subscriber port.
68 This must match that of the banyan_base backplane
69 :param publisher_port: banyan_base back plane publisher port.
70 This must match that of the
71 banyan_base backplane.
72 :param number_of_messages: number of message to transmit
73 :param process_name: Component identifier
74 :param loop_time: receive loop sleep time
75
76 """
77
78 # initialize the parent
79 super(EchoCmdClient, self).__init__(back_plane_ip_address=kwargs['back_plane_ip_address'],
80 subscriber_port=kwargs['subscriber_port'],
81 publisher_port=kwargs['publisher_port'],
82 process_name=kwargs['process_name'],
83 loop_time=kwargs['loop_time'])
84
85 # accept banyan messages with the topic of reply
86 self.set_subscriber_topic('reply')
87
88 # sequence number of messages
89 self.message_number = kwargs['number_of_messages']
90
91 # number of messages to send
92 self.number_of_messages = kwargs['number_of_messages']
93
94 # send the first message - make sure that the server is already started
95 self.publish_payload({'message_number': self.message_number}, 'echo')
96
97 # get the reply messages
98 try:
99 self.receive_loop()
100 except KeyboardInterrupt:
101 self.clean_up()
102 sys.exit(0)
Adding A Startup Function After The Class Definition
Here, we create a function that follows the class definition. This function, called echo_cmdline_client(), will instantiate the class and parse any command-line options that the user may have specified.
This function is contained within lines 125 to 168.
Line 126 creates an instance of an ArgumentParser.
Lines 129 through 144 create the command-line options and adds them to the argument parser.
Line 146 creates a variable called args and populates args with the values for all of the command-line options.
Lines 148 through 155 retrieve the option values using the defaults or the values provided by the user. The options are then added to a Python dictionary called kw_options.
Line 158 instantiates the class, passing in the kw_options.
Lines 161 through 164 add a signal handler to trap a user entered Control-C.
Line 172 calls the echo_cmdline_client function on line 125 to invoke the client.
125 def echo_cmdline_client():
126 parser = argparse.ArgumentParser()
127 # allow user to bypass the IP address auto-discovery.
128 # This is necessary if the component resides on a computer
129 # other than the computing running the backplane.
130 parser.add_argument("-b", dest="back_plane_ip_address", default="None",
131 help="None or IP address used by Back Plane")
132 parser.add_argument("-m", dest="number_of_messages", default="10",
133 help="Number of messages to publish")
134 # allow the user to specify a name for the component and have it shown on the console banner.
135 # modify the default process name to one you wish to see on the banner.
136 # change the default in the derived class to set the name
137 parser.add_argument("-n", dest="process_name", default="EchoCmdClient",
138 help="Set process name in banner")
139 parser.add_argument("-p", dest="publisher_port", default='43124',
140 help="Publisher IP port")
141 parser.add_argument("-s", dest="subscriber_port", default='43125',
142 help="Subscriber IP port")
143 parser.add_argument("-t", dest="loop_time", default=".1",
144 help="Event Loop Timer in seconds")
145
146 args = parser.parse_args()
147
148 if args.back_plane_ip_address == 'None':
149 args.back_plane_ip_address = None
150 kw_options = {'back_plane_ip_address': args.back_plane_ip_address,
151 'number_of_messages': int(args.number_of_messages),
152 'publisher_port': args.publisher_port,
153 'subscriber_port': args.subscriber_port,
154 'process_name': args.process_name,
155 'loop_time': float(args.loop_time)}
156
157 # replace with the name of your class
158 EchoCmdClient(**kw_options)
159
160
161 # signal handler function called when Control-C occurs
162 def signal_handler(sig, frame):
163 raise KeyboardInterrupt
164
165
166 # listen for SIGINT
167 signal.signal(signal.SIGINT, signal_handler)
168 signal.signal(signal.SIGTERM, signal_handler)
169
170
171 if __name__ == '__main__':
172 echo_cmdline_client()
Using The -m Option
We can now specify the number of messages the client produces while accepting the other parameters' default values.
Ensuring that the backplane and server are already running, we can start the new client, asking it to produce 20 messages.
python3 echo_cmdline_client.py -m 20
This command will create 20 messages.
Here is what the client console displays after running this command.
Copyright (C) 2017-2020 Alan Yorinks All Rights Reserved