Skip to content

Commit be4036a

Browse files
committed
Added CLI.
1 parent b261965 commit be4036a

File tree

2 files changed

+210
-2
lines changed

2 files changed

+210
-2
lines changed

setup.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33
# Setup definitions
44
setup(
55
name="python-tinylink",
6-
version="1.0",
6+
version="1.1",
77
description="Frame-based streaming protocol for embedded applications.",
88
author="Bas Stottelaar",
99
author_email="basstottelaar@gmail.com",
1010
py_modules=["tinylink"],
1111
license = "MIT",
12-
keywords = "python embedded arm tinylink streaming serial",
12+
keywords = "python embedded arm arduino tinylink streaming serial",
1313
test_suite="tests",
14+
entry_points={
15+
"console_scripts": [
16+
"tinylink = tinylink.cli:run",
17+
]
18+
},
1419
classifiers = [
1520
"Development Status :: 5 - Production/Stable",
1621
"Intended Audience :: Developers",

tinylink/cli.py

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
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

Comments
 (0)