Skip to content

Commit

Permalink
Fix a couple problems.
Browse files Browse the repository at this point in the history
- Fail example scripts if an exception is raised at any point.
- Rework `_crc_add` to produce a reliable CRC. This requires us to rely more on
string manipulation and less on python dicts. Prior to this commit, the CRC was
computed over the string representation of the request (via `json.dumps`) and
then it was added to the request dict. Then, when it was time to serialize the
request for transmission and we converted the dict to a string again, because
the CRC field had been added, the order of the fields changed and the CRC was
invalidated. This is because Python dicts are fundamentally unordered
collections.
- Add missing timeout reset to `OpenSerial`'s `receive` method.
- Increase cpy_example.py UART RX buffer size. I was seeing the end of responses
get cut off with the default size (64). Increased to 128.
  • Loading branch information
haydenroche5 committed Oct 1, 2023
1 parent 0a1f2e2 commit 0a89ebe
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 80 deletions.
42 changes: 9 additions & 33 deletions examples/notecard-basics/cpy_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,6 @@
import busio # noqa: E402


def NotecardExceptionInfo(exception):
"""Construct a formatted Exception string.
Args:
exception (Exception): An exception object.
Returns:
string: a summary of the exception with line number and details.
"""
name = exception.__class__.__name__
return sys.platform + ": " + name \
+ ": " + " ".join(map(str, exception.args))


def configure_notecard(card, product_uid):
"""Submit a simple JSON-based request to the Notecard.
Expand All @@ -39,11 +25,7 @@ def configure_notecard(card, product_uid):
req["product"] = product_uid
req["mode"] = "continuous"

try:
card.Transaction(req)
except Exception as exception:
print("Transaction error: " + NotecardExceptionInfo(exception))
time.sleep(5)
card.Transaction(req)


def get_temp_and_voltage(card):
Expand All @@ -53,20 +35,13 @@ def get_temp_and_voltage(card):
card (object): An instance of the Notecard class
"""
temp = 0
voltage = 0

try:
req = {"req": "card.temp"}
rsp = card.Transaction(req)
temp = rsp["value"]
req = {"req": "card.temp"}
rsp = card.Transaction(req)
temp = rsp["value"]

req = {"req": "card.voltage"}
rsp = card.Transaction(req)
voltage = rsp["value"]
except Exception as exception:
print("Transaction error: " + NotecardExceptionInfo(exception))
time.sleep(5)
req = {"req": "card.voltage"}
rsp = card.Transaction(req)
voltage = rsp["value"]

return temp, voltage

Expand All @@ -75,7 +50,8 @@ def run_example(product_uid, use_uart=True):
"""Connect to Notcard and run a transaction test."""
print("Opening port...")
if use_uart:
port = busio.UART(board.TX, board.RX, baudrate=9600)
port = busio.UART(board.TX, board.RX, baudrate=9600,
receiver_buffer_size=128)
else:
port = busio.I2C(board.SCL, board.SDA)

Expand Down
41 changes: 8 additions & 33 deletions examples/notecard-basics/mpy_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,6 @@
from machine import Pin


def NotecardExceptionInfo(exception):
"""Construct a formatted Exception string.
Args:
exception (Exception): An exception object.
Returns:
string: a summary of the exception with line number and details.
"""
name = exception.__class__.__name__
return sys.platform + ": " + name + ": " \
+ " ".join(map(str, exception.args))


def configure_notecard(card, product_uid):
"""Submit a simple JSON-based request to the Notecard.
Expand All @@ -41,11 +27,7 @@ def configure_notecard(card, product_uid):
req["product"] = product_uid
req["mode"] = "continuous"

try:
card.Transaction(req)
except Exception as exception:
print("Transaction error: " + NotecardExceptionInfo(exception))
time.sleep(5)
card.Transaction(req)


def get_temp_and_voltage(card):
Expand All @@ -55,20 +37,13 @@ def get_temp_and_voltage(card):
card (object): An instance of the Notecard class
"""
temp = 0
voltage = 0

try:
req = {"req": "card.temp"}
rsp = card.Transaction(req)
temp = rsp["value"]

req = {"req": "card.voltage"}
rsp = card.Transaction(req)
voltage = rsp["value"]
except Exception as exception:
print("Transaction error: " + NotecardExceptionInfo(exception))
time.sleep(5)
req = {"req": "card.temp"}
rsp = card.Transaction(req)
temp = rsp["value"]

req = {"req": "card.voltage"}
rsp = card.Transaction(req)
voltage = rsp["value"]

return temp, voltage

Expand Down
26 changes: 18 additions & 8 deletions notecard/notecard.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def __init__(self, debug=False):
self._card_supports_crc = False
self._reset_required = True

def _crc_add(self, req, seq_number):
def _crc_add(self, req_string, seq_number):
"""Add a CRC field to the request.
The CRC field also contains a sequence number and has this format:
Expand All @@ -139,10 +139,18 @@ def _crc_add(self, req, seq_number):
SSSS is the sequence number encoded as a string of 4 hex digits.
CCCCCCCC is the CRC32 encoded as a string of 8 hex digits.
"""
req_bytes = json.dumps(req, separators=(',', ':')).encode('utf-8')
req_bytes = req_string.encode('utf-8')
crc_hex = '{:08x}'.format(crc32(req_bytes))
seq_number_hex = '{:04x}'.format(seq_number)
req['crc'] = f'{seq_number_hex}:{crc_hex}'
crc_field = f'"crc":"{seq_number_hex}:{crc_hex}"'
req_string_w_crc = req_string[:-1]
if req_string[-2] == '{':
req_string_w_crc += f'{crc_field}'
else:
req_string_w_crc += f',{crc_field}'
req_string_w_crc += '}'

return req_string_w_crc

def _crc_error(self, rsp_bytes):
"""Check the CRC in a Notecard response."""
Expand Down Expand Up @@ -199,12 +207,13 @@ def _prepare_request(self, req):
rsp_expected = 'req' in req

# If this is a request and not a command, add a CRC.
req_string = json.dumps(req, separators=(',', ':'))
if rsp_expected:
self._crc_add(req, self._last_request_seq_number)
req_string = self._crc_add(req_string,
self._last_request_seq_number)

# Serialize the JSON request to a string, removing any unnecessary
# whitespace.
req_string = json.dumps(req, separators=(',', ':'))
if self._debug:
print(req_string)

Expand Down Expand Up @@ -320,7 +329,7 @@ def Transaction(self, req, lock=True):
if '{io}' in rsp_json['err']:
if self._debug:
print(('Response has error field indicating I/O'
' error.'))
f' error: {rsp_json}'))

error = True
retries_left -= 1
Expand All @@ -329,8 +338,8 @@ def Transaction(self, req, lock=True):
elif '{bad-bin}' in rsp_json['err']:
if self._debug:
print(('Response has error field indicating '
'binary I/O error. Not eligible for '
'retry.'))
f'binary I/O error: {rsp_json}'))
print('Not eligible for retry.')

error = True
break
Expand Down Expand Up @@ -433,6 +442,7 @@ def receive(self, timeout_secs=CARD_INTRA_TRANSACTION_TIMEOUT_SEC,
time.sleep(.001)

timeout_secs = CARD_INTRA_TRANSACTION_TIMEOUT_SEC
start = start_timeout()
byte = self._read_byte()
data.extend(byte)
received_newline = byte == b'\n'
Expand Down
14 changes: 8 additions & 6 deletions test/test_notecard.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,25 @@ def test_txn_manager_is_valid_after_pins_set(self):
# _crc_add tests
def test_crc_add_adds_a_crc_field(self):
card = notecard.Notecard()
req = {'req': 'hub.status'}
req = '{"req":"hub.status"}'

card._crc_add(req, 0)
req_string = card._crc_add(req, 0)

assert 'crc' in req
req_json = json.loads(req_string)
assert 'crc' in req_json

def test_crc_add_formats_the_crc_field_correctly(self):
card = notecard.Notecard()
req = {'req': 'hub.status'}
req = '{"req":"hub.status"}'
seq_number = 37

card._crc_add(req, seq_number)
req_string = card._crc_add(req, seq_number)

req_json = json.loads(req_string)
# The format should be SSSS:CCCCCCCC, where S and C are hex digits
# comprising the sequence number and CRC32, respectively.
pattern = r'^[0-9A-Fa-f]{4}:[0-9A-Fa-f]{8}$'
assert re.match(pattern, req['crc'])
assert re.match(pattern, req_json['crc'])

# _crc_error tests.
@pytest.mark.parametrize('crc_supported', [False, True])
Expand Down

0 comments on commit 0a89ebe

Please sign in to comment.