Skip to content

Commit a01fb50

Browse files
committed
Clean up serial comms code.
- Add a serial_lock decorator that can be used on functions that attempt to acquire a lock on the serial port. - Make serialReadByte, serialReset, serialCommand, and serialTransaction member functions of OpenSerial instead of being free functions. - Refactor serialCommand and serialTransaction (now renamed Command and Transaction, respectively) to unify common code in a new internal function, _transmit.
1 parent 86fa832 commit a01fb50

File tree

2 files changed

+84
-122
lines changed

2 files changed

+84
-122
lines changed

notecard/notecard.py

Lines changed: 83 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
CARD_REQUEST_SEGMENT_DELAY_MS = 250
6060

6161

62-
def prepareRequest(req, debug=False):
62+
def _prepare_request(req, debug=False):
6363
"""Format the request string as a JSON object and add a newline."""
6464
req_json = json.dumps(req)
6565
if debug:
@@ -69,90 +69,20 @@ def prepareRequest(req, debug=False):
6969
return req_json
7070

7171

72-
def serialReadByte(port):
73-
"""Read a single byte from a Notecard."""
74-
if sys.implementation.name == 'micropython':
75-
if not port.any():
76-
return None
77-
elif sys.implementation.name == 'cpython':
78-
if port.in_waiting == 0:
79-
return None
80-
return port.read(1)
72+
def serial_lock(fn):
73+
"""Attempt to get a lock on the serial channel used for Notecard comms."""
8174

82-
83-
def serialReset(port):
84-
"""Send a reset command to a Notecard."""
85-
for i in range(10):
86-
try:
87-
port.write(b'\n')
88-
except:
89-
continue
90-
time.sleep(0.5)
91-
somethingFound = False
92-
nonControlCharFound = False
93-
while True:
94-
data = serialReadByte(port)
95-
if (data is None) or (data == b''):
96-
break
97-
somethingFound = True
98-
if data[0] >= 0x20:
99-
nonControlCharFound = True
100-
if somethingFound and not nonControlCharFound:
101-
break
75+
def decorator(self, *args, **kwargs):
76+
if use_serial_lock:
77+
try:
78+
with self.lock.acquire(timeout=5):
79+
return fn(self, *args, **kwargs)
80+
except Timeout:
81+
raise Exception('Notecard in use')
10282
else:
103-
raise Exception("Notecard not responding")
83+
return fn(self, *args, **kwargs)
10484

105-
106-
def serialTransaction(port, req, debug, txn_manager=None):
107-
"""Perform a single write to and read from a Notecard."""
108-
req_json = prepareRequest(req, debug)
109-
110-
transaction_timeout_secs = 30
111-
if txn_manager:
112-
txn_manager.start(transaction_timeout_secs)
113-
114-
seg_off = 0
115-
seg_left = len(req_json)
116-
while True:
117-
seg_len = seg_left
118-
if seg_len > CARD_REQUEST_SEGMENT_MAX_LEN:
119-
seg_len = CARD_REQUEST_SEGMENT_MAX_LEN
120-
121-
port.write(req_json[seg_off:seg_off + seg_len].encode('utf-8'))
122-
seg_off += seg_len
123-
seg_left -= seg_len
124-
if seg_left == 0:
125-
break
126-
time.sleep(CARD_REQUEST_SEGMENT_DELAY_MS / 1000)
127-
128-
if txn_manager:
129-
txn_manager.stop()
130-
131-
rsp_json = port.readline()
132-
if debug:
133-
print(rsp_json.rstrip())
134-
135-
rsp = json.loads(rsp_json)
136-
return rsp
137-
138-
139-
def serialCommand(port, req, debug):
140-
"""Perform a single write to and read from a Notecard."""
141-
req_json = prepareRequest(req, debug)
142-
143-
seg_off = 0
144-
seg_left = len(req_json)
145-
while True:
146-
seg_len = seg_left
147-
if seg_len > CARD_REQUEST_SEGMENT_MAX_LEN:
148-
seg_len = CARD_REQUEST_SEGMENT_MAX_LEN
149-
150-
port.write(req_json[seg_off:seg_off + seg_len].encode('utf-8'))
151-
seg_off += seg_len
152-
seg_left -= seg_len
153-
if seg_left == 0:
154-
break
155-
time.sleep(CARD_REQUEST_SEGMENT_DELAY_MS / 1000)
85+
return decorator
15686

15787

15888
class Notecard:
@@ -178,7 +108,7 @@ def __init__(self):
178108
self._user_agent['os_family'] = os.uname().machine
179109
self._transaction_manager = None
180110

181-
def _preprocessReq(self, req):
111+
def _preprocess_req(self, req):
182112
"""Inspect the request for hub.set and add the User Agent."""
183113
if 'hub.set' in req.values():
184114
# Merge the User Agent to send along with the hub.set request.
@@ -210,51 +140,83 @@ def SetTransactionPins(self, rtx_pin, ctx_pin):
210140
class OpenSerial(Notecard):
211141
"""Notecard class for Serial communication."""
212142

143+
def _transmit(self, req):
144+
req = self._preprocess_req(req)
145+
req_json = _prepare_request(req, self._debug)
146+
147+
transaction_timeout_secs = 30
148+
if self._transaction_manager:
149+
self._transaction_manager.start(transaction_timeout_secs)
150+
151+
seg_off = 0
152+
seg_left = len(req_json)
153+
while True:
154+
seg_len = seg_left
155+
if seg_len > CARD_REQUEST_SEGMENT_MAX_LEN:
156+
seg_len = CARD_REQUEST_SEGMENT_MAX_LEN
157+
158+
self.uart.write(req_json[seg_off:seg_off + seg_len].encode('utf-8'))
159+
seg_off += seg_len
160+
seg_left -= seg_len
161+
if seg_left == 0:
162+
break
163+
time.sleep(CARD_REQUEST_SEGMENT_DELAY_MS / 1000)
164+
165+
if self._transaction_manager:
166+
self._transaction_manager.stop()
167+
168+
def _read_byte(self):
169+
"""Read a single byte from the Notecard."""
170+
if sys.implementation.name == 'micropython':
171+
if not self.uart.any():
172+
return None
173+
elif sys.implementation.name == 'cpython':
174+
if self.uart.in_waiting == 0:
175+
return None
176+
return self.uart.read(1)
177+
178+
@serial_lock
213179
def Command(self, req):
214-
"""Perform a Notecard command and exit with no response."""
215-
req = self._preprocessReq(req)
180+
"""Send a command to the Notecard. The Notecard response is ignored."""
216181
if 'cmd' not in req:
217182
raise Exception("Please use 'cmd' instead of 'req'")
218183

219-
if use_serial_lock:
220-
try:
221-
self.lock.acquire(timeout=5)
222-
serialCommand(self.uart, req, self._debug)
223-
except Timeout:
224-
raise Exception("Notecard in use")
225-
finally:
226-
self.lock.release()
227-
else:
228-
serialCommand(self.uart, req, self._debug)
184+
self._transmit(req)
229185

186+
@serial_lock
230187
def Transaction(self, req):
231188
"""Perform a Notecard transaction and return the result."""
232-
req = self._preprocessReq(req)
233-
if use_serial_lock:
234-
try:
235-
self.lock.acquire(timeout=5)
236-
return serialTransaction(self.uart, req, self._debug,
237-
self._transaction_manager)
238-
except Timeout:
239-
raise Exception("Notecard in use")
240-
finally:
241-
self.lock.release()
242-
else:
243-
return serialTransaction(self.uart, req, self._debug,
244-
self._transaction_manager)
189+
self._transmit(req)
190+
191+
rsp_json = self.uart.readline()
192+
if self._debug:
193+
print(rsp_json.rstrip())
194+
195+
rsp = json.loads(rsp_json)
196+
return rsp
245197

198+
@serial_lock
246199
def Reset(self):
247200
"""Reset the Notecard."""
248-
if use_serial_lock:
201+
for i in range(10):
249202
try:
250-
self.lock.acquire(timeout=5)
251-
serialReset(self.uart)
252-
except Timeout:
253-
raise Exception("Notecard in use")
254-
finally:
255-
self.lock.release()
256-
else:
257-
serialReset(self.uart)
203+
self.uart.write(b'\n')
204+
except:
205+
continue
206+
time.sleep(0.5)
207+
somethingFound = False
208+
nonControlCharFound = False
209+
while True:
210+
data = self._read_byte()
211+
if (data is None) or (data == b''):
212+
break
213+
somethingFound = True
214+
if data[0] >= 0x20:
215+
nonControlCharFound = True
216+
if somethingFound and not nonControlCharFound:
217+
break
218+
else:
219+
raise Exception('Notecard not responding')
258220

259221
def __init__(self, uart_id, debug=False):
260222
"""Initialize the Notecard before a reset."""
@@ -299,11 +261,11 @@ def _sendPayload(self, json):
299261

300262
def Command(self, req):
301263
"""Perform a Notecard command and exit with no response."""
302-
req = self._preprocessReq(req)
264+
req = self._preprocess_req(req)
303265
if 'cmd' not in req:
304266
raise Exception("Please use 'cmd' instead of 'req'")
305267

306-
req_json = prepareRequest(req, self._debug)
268+
req_json = _prepare_request(req, self._debug)
307269

308270
while not self.lock():
309271
pass
@@ -315,9 +277,9 @@ def Command(self, req):
315277

316278
def Transaction(self, req):
317279
"""Perform a Notecard transaction and return the result."""
318-
req = self._preprocessReq(req)
280+
req = self._preprocess_req(req)
319281

320-
req_json = prepareRequest(req, self._debug)
282+
req_json = _prepare_request(req, self._debug)
321283
rsp_json = ""
322284

323285
while not self.lock():

test/test_notecard.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ def setUserAgentInfo(self, info=None):
462462
nCard = MockNotecard()
463463
orgReq = {"req": "hub.set"}
464464
nCard.SetAppUserAgent(info)
465-
req = nCard._preprocessReq(orgReq)
465+
req = nCard._preprocess_req(orgReq)
466466
return req
467467

468468
def test_amends_hub_set_request(self):

0 commit comments

Comments
 (0)