Get with File on P-ION: connect and transfer the way Ontrol's Niagara driver does
A real P-ION (fw 1.32) restarted on every Sox file get. Ontrol's Niagara 4 driver gets app.sab from the same device, so its settings are copied: - DASP hello asks for 256-byte packets (idealMax/absMax), so file chunks are 238 bytes - the message-length header (0x81) goes to Orion only, decided once the platform id is read - the app is saved on the device before app.sab is read, the save resent every 5 s rather than 1 s (a P-ION save takes 2-3+ s) - the file open is resent every 15 s; DASP gains a per-packet resend interval for this Tested on a P-ION at 192.168.1.252: save 3.2 s, app.sab + kits.scode + app.sax in 15.8 s, no resends, device stayed up. The unsaved-changes question became a notice, since the app is now always saved first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,18 @@ BUF_ID = 7
|
||||
STR_ID = 8
|
||||
|
||||
|
||||
# DASP resend intervals (seconds) for requests a device can be slow to take, as Ontrol's
|
||||
# Niagara driver sets them: a P-ION saving a large app takes 2-3+ s, and a file open can
|
||||
# take 10+ s on some devices. Resent every second instead, the device is sent copies
|
||||
# while it is still busy with the first.
|
||||
SAVE_SEND_RETRY = 5.0
|
||||
FILE_OPEN_SEND_RETRY = 15.0
|
||||
|
||||
# The DASP packet sizes Ontrol's Niagara driver asks for in its hello.
|
||||
IDEAL_MAX = 256
|
||||
ABS_MAX = 256
|
||||
|
||||
|
||||
class SoxException(Exception):
|
||||
pass
|
||||
|
||||
@@ -464,6 +476,10 @@ class SoxClient:
|
||||
# Same 512-byte limit as read_schema: whatever is cut off is left
|
||||
# blank rather than failing the connect.
|
||||
platform_id = res.try_str() or ""
|
||||
# Ontrol's message-length header is for Orion only; the Niagara driver never
|
||||
# sends it to a P-ION or R-ION.
|
||||
if self._session is not None:
|
||||
self._session.include_message_len = platform_id.startswith("ontrol-orion")
|
||||
scode_flags = res.u1() if res.remaining > 0 else 0
|
||||
kit_versions = []
|
||||
for p in parts:
|
||||
@@ -662,13 +678,13 @@ class SoxClient:
|
||||
|
||||
# ── Invoke Action ───────────────────────────────────────────
|
||||
|
||||
def invoke(self, comp_id, slot_id, type_id=None, arg=None):
|
||||
def invoke(self, comp_id, slot_id, type_id=None, arg=None, send_retry=None):
|
||||
req = _prepare_request(ord('i'))
|
||||
req.write_u2(comp_id)
|
||||
req.write_u1(slot_id)
|
||||
if arg is not None and type_id is not None:
|
||||
encode_value(req, type_id, arg)
|
||||
res = self.request(req)
|
||||
res = self.request(req, send_retry)
|
||||
res.check_response('I')
|
||||
|
||||
# ── Add Component ───────────────────────────────────────────
|
||||
@@ -934,7 +950,7 @@ class SoxClient:
|
||||
opened = closed = False
|
||||
try:
|
||||
try:
|
||||
res = self.request(req)
|
||||
res = self.request(req, FILE_OPEN_SEND_RETRY)
|
||||
except SoxException as e:
|
||||
# A P-ION on firmware 1.32 never answers a file get -- it drops off
|
||||
# the network for ~20 s, as a restart would -- even for a file it
|
||||
@@ -986,16 +1002,21 @@ class SoxClient:
|
||||
|
||||
# ── Networking ──────────────────────────────────────────────
|
||||
|
||||
def request(self, req):
|
||||
results = self.requests([req])
|
||||
def request(self, req, send_retry=None):
|
||||
results = self.requests([req], send_retry)
|
||||
return results[0]
|
||||
|
||||
def requests(self, reqs):
|
||||
def requests(self, reqs, send_retry=None):
|
||||
"""Sends the batch and waits for every reply.
|
||||
|
||||
send_retry (seconds) sets the DASP resend interval of these datagrams, for a
|
||||
request the device is known to be slow to take.
|
||||
"""
|
||||
if self.is_closed:
|
||||
raise SoxException("SoxClient closed")
|
||||
|
||||
if len(reqs) > 255:
|
||||
return self._chunk_requests(reqs)
|
||||
return self._chunk_requests(reqs, send_retry)
|
||||
|
||||
with self._lock:
|
||||
for i, req in enumerate(reqs):
|
||||
@@ -1006,7 +1027,7 @@ class SoxClient:
|
||||
self._req_event.clear()
|
||||
|
||||
for req in reqs:
|
||||
self._session.send(req.to_bytes())
|
||||
self._session.send(req.to_bytes(), send_retry)
|
||||
|
||||
deadline = time.monotonic() + 30.0
|
||||
while True:
|
||||
@@ -1030,11 +1051,11 @@ class SoxClient:
|
||||
self._responses = None
|
||||
raise SoxException("Request timeout")
|
||||
|
||||
def _chunk_requests(self, reqs):
|
||||
def _chunk_requests(self, reqs, send_retry=None):
|
||||
results = []
|
||||
for i in range(0, len(reqs), 255):
|
||||
chunk = reqs[i:i + 255]
|
||||
results.extend(self.requests(chunk))
|
||||
results.extend(self.requests(chunk, send_retry))
|
||||
return results
|
||||
|
||||
# called by receiver thread
|
||||
|
||||
Reference in New Issue
Block a user