1+ import sys
2+ import csv
3+ import signal
4+ import serial
5+ import select
6+ import struct
7+ import tinylink
8+ import argparse
9+ import cStringIO
10+ import time
11+
12+ def run ():
13+ """
14+ Entry point for console script.
15+ """
16+ sys .exit (main ())
17+
18+ def parse_arguments ():
19+ """
20+ Create and parse command line arguments.
21+ """
22+
23+ parser = argparse .ArgumentParser ()
24+
25+ # Add option
26+ parser .add_argument ("port" , type = str , help = "serial port" )
27+ parser .add_argument ("baudrate" , type = int , default = 9600 ,
28+ help = "serial baudrate" )
29+ parser .add_argument ("--length" , type = int , default = 2 ** 16 ,
30+ help = "maximum length of frame" )
31+ parser .add_argument ("--endianness" , type = str , default = "little" ,
32+ choices = ["big" , "little" ], help = "maximum length of frame" )
33+
34+ # Parse command line
35+ return parser .parse_args (), parser
36+
37+ def dump (prefix , data ):
38+ """
39+ Dump data as two hex columns
40+ """
41+
42+ result = []
43+ length = len (data )
44+
45+ for i in xrange (0 , length , 16 ):
46+ hexstr = bytestr = ""
47+
48+ for j in xrange (0 , 16 ):
49+ if i + j < length :
50+ b = ord (data [i + j ])
51+ hexstr += "%02x " % b
52+ bytestr += data [i + j ] if 0x20 <= b < 0x7F else "."
53+ else :
54+ hexstr += " "
55+
56+ if (j % 4 ) == 3 :
57+ hexstr += " "
58+
59+ result .append (prefix + " " + hexstr + bytestr )
60+
61+ # Return concatenated string
62+ return "\n " .join (result )
63+
64+ def process_link (link ):
65+ """
66+ Process incoming link data.
67+ """
68+
69+ frames = link .read ()
70+
71+ # Print received frames
72+ for frame in frames :
73+ sys .stdout .write ("### Type = %s\n " % frame .__class__ .__name__ )
74+ sys .stdout .write ("### Flags = 0x%04x\n " % frame .flags )
75+
76+ if type (frame ) != tinylink .ResetFrame :
77+ sys .stdout .write ("### Lenght = %d\n " % len (frame .data ))
78+ sys .stdout .write (dump ("<<<" , frame .data ) + "\n \n " )
79+ else :
80+ sys .stdout .write ("\n " )
81+
82+ def process_stdin (link ):
83+ """
84+ Process stdin commands.
85+ """
86+
87+ command = sys .stdin .readline ()
88+
89+ # End of file
90+ if len (command ) == 0 :
91+ return False
92+
93+ # Very simple command parser
94+ items = list (csv .reader (cStringIO .StringIO (command .strip ()), delimiter = " " ))
95+
96+ if not items :
97+ return
98+
99+ # Initialize state and start parsing
100+ frame = tinylink .BaseFrame ()
101+ repeat = 1
102+ pack = "B"
103+
104+ try :
105+ for item in items [0 ]:
106+ if item [0 ] == "/" :
107+ k , v = item [1 :].split ("=" )
108+
109+ if k == "flags" :
110+ frame .flags = int (v , 0 )
111+ elif k == "pack" :
112+ pack = v
113+ elif k == "wait" :
114+ time .sleep (float (v ))
115+ elif k == "repeat" :
116+ repeat = int (v )
117+ else :
118+ raise ValueError ("Unkown option: %s" % key )
119+ else :
120+ try :
121+ # Assume it's a float
122+ value = struct .pack (link .endianness + pack , float (item ))
123+ except :
124+ try :
125+ # Assume it's an int
126+ value = struct .pack (link .endianness + pack ,
127+ int (item , 0 ))
128+ except ValueError :
129+ # Assume as string
130+ value = ""
131+
132+ for b in item :
133+ value += struct .pack (link .endianness + "B" ,
134+ ord (b ))
135+
136+ # Concat to frame
137+ frame .data = (frame .data or "" ) + value
138+ except Exception as e :
139+ sys .stdout .write ("Parse exception: %s\n " % e )
140+ return
141+
142+ # Output the data
143+ for i in xrange (repeat ):
144+ sys .stdout .write ("### Flags = 0x%04x\n " % frame .flags )
145+
146+ if frame .data :
147+ sys .stdout .write ("### Lenght = %d\n " % len (frame .data ))
148+ sys .stdout .write (dump (">>>" , frame .data ) + "\n \n " )
149+
150+ # Send frame
151+ try :
152+ link .write_frame (frame )
153+ except ValueError as e :
154+ sys .stdout .write ("Could not send frame: %s\n " % e )
155+ return
156+
157+ def main ():
158+ """
159+ Main entry point.
160+ """
161+
162+ arguments , parser = parse_arguments ()
163+
164+ # Open serial port and create link
165+ if arguments .endianness == "little" :
166+ endianness = tinylink .LITTLE_ENDIAN
167+ else :
168+ endianness = tinylink .BIG_ENDIAN
169+
170+ handle = serial .Serial (arguments .port , baudrate = arguments .baudrate )
171+ link = tinylink .TinyLink (handle , max_length = arguments .length ,
172+ endianness = endianness )
173+
174+ # Loop until finished
175+ try :
176+ # Input indicator
177+ sys .stdout .write ("--> " )
178+ sys .stdout .flush ()
179+
180+ while True :
181+ readables , _ , _ = select .select ([handle , sys .stdin ], [], [])
182+
183+ # Read from serial port
184+ if handle in readables :
185+ process_link (link )
186+
187+ # Read from stdin
188+ if sys .stdin in readables :
189+ if process_stdin (link ) is False :
190+ break
191+
192+ # Input indicator
193+ sys .stdout .write ("--> " )
194+ sys .stdout .flush ()
195+ except KeyboardInterrupt :
196+ handle .close ()
197+
198+ # Done
199+ return 0
200+
201+ # E.g. `python tinylink_cli.py /dev/tty.usbmodem1337 --baudrate 9600'
202+ if __name__ == "__main__" :
203+ run ()
0 commit comments