I left the notecard powered off for a few hours and now it’s back to behaving normal again. (Power-cycles of about minute did not fix things). Leaving it powered on but in “off” mode overnight, also didn’t address the issue. Just to be clear, this is still with the default 250ms SEGMENT_DELAY.
The documentation talks about a continuous mode session duration
. Perhaps the continuous mode session is getting corrupted somehow and I need to wait until the session has hit this timeout before things work correctly again. The documents don’t list what the default duration
is.
It works when hub.status
returns:
{'connected': True, 'status': 'connected (session open) {connected}'}
But it appears to not work when hub.status
returns:
{"status":"connected {connected-closed}"}
Is it possible that aweb.post
with a payload of 8kB is what’s causing the issue? The documentation doesn’t state a maximum payload size (for either a normal post or a fragment post). I started playing around and running with fragment sizes of 1kB and 2kB appeared to work reliably.
@bsatrom @scjerry at least with 1kB payloads, reducing the SEGMENT_DELAY doesn’t have a measurable effect because each call to send a web.post
fragment is measuring to have a minimum time of about 0.6 - 1.0 seconds due to overhead in the hub communication. With 8kB payloads and a 50ms SEGMENT_DELAY I could send 64kB in ~30 seconds (i.e. about 4 seconds per 8KB chunk).
Here is the code I’m currently using:
class temporary_mode():
def __init__(self, card, mode, wait_for_connection=True):
self.card = card
self.mode = mode
self.current_mode = None
self.wait_for_connection = wait_for_connection
def __enter__(self):
# get the current mode
req = {
"req": "hub.get",
}
rsp = self.card.Transaction(req)
self.current_mode = rsp.get("mode")
if self.current_mode != self.mode:
# set the new mode
req = {
"req": "hub.set",
"mode": self.mode
}
rsp = self.card.Transaction(req)
logging.info("Setting to %s mode %s", self.mode, rsp)
else:
logging.info("Card is already in mode %s with response %s", self.mode, rsp)
# wait for the notecard to become connected
rsp = {}
while self.wait_for_connection:
req = {"req": "hub.status"}
rsp = self.card.Transaction(req)
logging.info("Checking connection %s", rsp)
# 'connected {connected-closed}' might not be valid, but I haven't
# figured out how to get the notecard out of that state
if rsp.get("connected") == True or rsp.get("status") == 'connected {connected-closed}':
break
time.sleep(5)
def __exit__(self, *args, **kwargs):
if self.current_mode != self.mode:
# restore the old mode
req = {
"req": "hub.set",
"mode": self.current_mode
}
rsp = self.card.Transaction(req)
logging.info("Restoring original mode %s response %s", self.current_mode, rsp)
from notecard import notecard as _notecard
class temporary_segment_delay():
"""
Temporarily change CARD_REQUEST_SEGMENT_DELAY_MS.
"""
def __init__(self, delay_ms):
if delay_ms < 25:
raise ValueError("segment delay less than 25ms could result in unstable operation")
self.initial_delay_ms = _notecard.CARD_REQUEST_SEGMENT_DELAY_MS
self.new_delay_ms = delay_ms
def __enter__(self):
logging.info("setting CARD_REQUEST_SEGMENT_DELAY_MS from %s to %s", self.initial_delay_ms, self.new_delay_ms)
_notecard.CARD_REQUEST_SEGMENT_DELAY_MS = self.new_delay_ms
def __exit__(self, *args, **kwargs):
logging.info("restoring CARD_REQUEST_SEGMENT_DELAY_MS to %s", self.initial_delay_ms)
_notecard.CARD_REQUEST_SEGMENT_DELAY_MS = self.initial_delay_ms
def web_post(card, route, payload, name=None, chunk_size=1024, content=None):
offset = 0
fragmented = ( len(payload) > chunk_size )
s = time.time()
# nested with to be Py 2 compatible without using contextlib
# web.post requires continuous mode
with temporary_mode(card, "continuous"):
# Use a faster segment delay on web.post
with temporary_segment_delay(50):
while offset < len(payload):
req = {"req": "web.post"}
req["route"] = route
if name:
req["name"] = name
if content:
req['content'] = content
if fragmented:
fragment = payload[offset:offset+chunk_size]
req["total"] = len(payload)
req["payload"] = b2a_base64( fragment, newline=False).decode("ascii")
req["status"] = hashlib.md5( fragment ).hexdigest()
req["offset"] = offset
req["verify"] = True
logging.debug("sending web.post fragment of length %s at offset %s", len(fragment), offset)
else:
req["payload"] = b2a_base64( payload, newline=False ).decode("ascii")
logging.debug("sending web.post of length %s", len(payload))
offset += chunk_size
rsp = card.Transaction(req)
logging.debug("web.post response %s", rsp)
# if data remains to be transmitted we expect a 100 request
if offset < len(payload) and rsp.get("result") != 100:
raise RuntimeError("error in fragmented web.post")
response_payload = None
if rsp.get("payload"):
response_payload = a2b_base64(rsp['payload']).decode("ascii")
e = time.time()
logging.debug("web.post took %s seconds for %s bytes", e-s, len(payload))
return rsp, response_payload