Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8c68f7b31 | ||
|
|
5acca522bf |
+2
-5
@@ -23,7 +23,6 @@ import zipfile
|
||||
|
||||
import app_state
|
||||
import sab_app
|
||||
import sox
|
||||
from ox_widget_properties import IMAGE_EXTENSIONS, ORION_KIT, RION_KIT
|
||||
|
||||
APP_SAB = "app.sab"
|
||||
@@ -163,14 +162,12 @@ def _source_entry(media, source_path):
|
||||
def save_app(client):
|
||||
"""Invokes App.save on the device and waits for it to finish.
|
||||
|
||||
Ontrol's Niagara driver does this before every app.sab get, resending the request
|
||||
only every 5 s: a P-ION saving a large app takes seconds, and 1 s resends of the
|
||||
request while it is busy are what knocked it over.
|
||||
Ontrol's Niagara driver does this before every app.sab get.
|
||||
"""
|
||||
comp = client.cache_get(0) or client.load(0)
|
||||
slots = client.slot_registry.get((comp.kit_id, comp.type_id)) or []
|
||||
slot = next((s for s in slots if s["name"] == "save" and s.get("is_action")), None)
|
||||
client.invoke(0, slot["id"] if slot else 0, send_retry=sox.SAVE_SEND_RETRY)
|
||||
client.invoke(0, slot["id"] if slot else 0)
|
||||
|
||||
|
||||
class BackupResult:
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ so later rebinding would only ever be seen by the module that did it.
|
||||
import configparser
|
||||
import os
|
||||
|
||||
VERSION = "0.0.0.144"
|
||||
VERSION = "0.0.0.146"
|
||||
PROPERTIES_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "editor.properties")
|
||||
DEFAULT_GRID_SIZE = 10
|
||||
DEFAULT_ADD_TIMESTAMP = True
|
||||
|
||||
@@ -456,7 +456,7 @@ def _to_ack_more(seq_more, max_bytes):
|
||||
|
||||
class _Packet:
|
||||
__slots__ = ("seq_num", "payload", "acked", "enqueued_time",
|
||||
"sent_time", "send_attempts", "send_retry", "next")
|
||||
"sent_time", "send_attempts", "next")
|
||||
|
||||
def __init__(self):
|
||||
self.seq_num = 0
|
||||
@@ -465,7 +465,6 @@ class _Packet:
|
||||
self.enqueued_time = 0.0
|
||||
self.sent_time = 0.0
|
||||
self.send_attempts = 1
|
||||
self.send_retry = None # seconds; None = the window's own interval
|
||||
self.next = None
|
||||
|
||||
|
||||
@@ -507,17 +506,17 @@ class SendWindow:
|
||||
self._tail = None
|
||||
self._cond.notify_all()
|
||||
|
||||
def send(self, payload, send_retry=None):
|
||||
def send(self, payload):
|
||||
p = _Packet()
|
||||
p.send_retry = send_retry
|
||||
p.enqueued_time = _ticks()
|
||||
p.payload = payload
|
||||
with self._cond:
|
||||
p.seq_num = self._seq_num & 0xFFFF
|
||||
self._seq_num = (self._seq_num + 1) & 0xFFFF
|
||||
|
||||
# sent_time is stamped inside the lock: send_retries() must not see the
|
||||
# packet in the window as never sent and resend it before its first send.
|
||||
self._enqueue(p)
|
||||
p.payload = payload
|
||||
p.sent_time = _ticks()
|
||||
self._session._send_raw(self._to_msg(p))
|
||||
|
||||
def _enqueue(self, p):
|
||||
@@ -531,6 +530,7 @@ class SendWindow:
|
||||
else:
|
||||
self._tail.next = p
|
||||
self._tail = p
|
||||
p.sent_time = _ticks()
|
||||
self._size += 1
|
||||
self._cond.notify_all()
|
||||
|
||||
@@ -620,15 +620,9 @@ class SendWindow:
|
||||
p = self._head
|
||||
while p is not None:
|
||||
if not p.acked:
|
||||
if p.send_retry is not None:
|
||||
# A request the device is slow to take -- a P-ION saving its
|
||||
# app, opening a file -- is resent at its own steady interval:
|
||||
# the 1 s default resends it while the device is still busy.
|
||||
interval = p.send_retry
|
||||
else:
|
||||
backoff_shift = min(p.send_attempts - 1, 32)
|
||||
interval = min(self.send_retry * (1 << backoff_shift),
|
||||
self.max_retry_backoff)
|
||||
backoff_shift = min(p.send_attempts - 1, 32)
|
||||
interval = min(self.send_retry * (1 << backoff_shift),
|
||||
self.max_retry_backoff)
|
||||
if now - p.sent_time >= interval:
|
||||
self._resend(p)
|
||||
oldest = max(oldest, now - p.enqueued_time)
|
||||
@@ -784,11 +778,10 @@ class DaspSession:
|
||||
|
||||
# ── Send / Receive ──────────────────────────────────────────
|
||||
|
||||
def send(self, payload, send_retry=None):
|
||||
"""Queues a datagram. send_retry (seconds) overrides the resend interval."""
|
||||
def send(self, payload):
|
||||
if self.is_closed:
|
||||
raise DaspException(f"Session closed: {self.close_cause}")
|
||||
self.send_window.send(payload, send_retry)
|
||||
self.send_window.send(payload)
|
||||
|
||||
def receive(self, timeout=1.0):
|
||||
if self.is_closed:
|
||||
|
||||
+1
-3
@@ -10,7 +10,6 @@ import xml.etree.ElementTree as ET
|
||||
|
||||
import app_state
|
||||
import schema
|
||||
import sox
|
||||
from sox import SoxClient, SoxComponent, TREE, CONFIG, RUNTIME, LINKS, ALL
|
||||
|
||||
|
||||
@@ -44,8 +43,7 @@ class DeviceSession:
|
||||
|
||||
def connect(self, host, port, user, password):
|
||||
"""DASP connect → read schema → load root. Called from background thread."""
|
||||
self.sox_client = SoxClient(host, int(port), user, password,
|
||||
ideal_max=sox.IDEAL_MAX, abs_max=sox.ABS_MAX)
|
||||
self.sox_client = SoxClient(host, int(port), user, password)
|
||||
self.sox_client.on_event = self._on_sox_event
|
||||
self.sox_client.checksum_resolver = schema.resolve_truncated_checksum
|
||||
self.sox_client.connect()
|
||||
|
||||
@@ -35,18 +35,6 @@ 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
|
||||
|
||||
@@ -476,8 +464,9 @@ 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.
|
||||
# Ontrol's message-length header is for Orion only, as in the Niagara driver.
|
||||
# A P-ION on firmware 1.32 takes it on most requests, but a file open sent with
|
||||
# it goes unanswered and the device drops off the network for ~20 s.
|
||||
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
|
||||
@@ -678,13 +667,13 @@ class SoxClient:
|
||||
|
||||
# ── Invoke Action ───────────────────────────────────────────
|
||||
|
||||
def invoke(self, comp_id, slot_id, type_id=None, arg=None, send_retry=None):
|
||||
def invoke(self, comp_id, slot_id, type_id=None, arg=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, send_retry)
|
||||
res = self.request(req)
|
||||
res.check_response('I')
|
||||
|
||||
# ── Add Component ───────────────────────────────────────────
|
||||
@@ -950,7 +939,7 @@ class SoxClient:
|
||||
opened = closed = False
|
||||
try:
|
||||
try:
|
||||
res = self.request(req, FILE_OPEN_SEND_RETRY)
|
||||
res = self.request(req)
|
||||
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
|
||||
@@ -1002,21 +991,17 @@ class SoxClient:
|
||||
|
||||
# ── Networking ──────────────────────────────────────────────
|
||||
|
||||
def request(self, req, send_retry=None):
|
||||
results = self.requests([req], send_retry)
|
||||
def request(self, req):
|
||||
results = self.requests([req])
|
||||
return results[0]
|
||||
|
||||
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.
|
||||
"""
|
||||
def requests(self, reqs):
|
||||
"""Sends the batch and waits for every reply."""
|
||||
if self.is_closed:
|
||||
raise SoxException("SoxClient closed")
|
||||
|
||||
if len(reqs) > 255:
|
||||
return self._chunk_requests(reqs, send_retry)
|
||||
return self._chunk_requests(reqs)
|
||||
|
||||
with self._lock:
|
||||
for i, req in enumerate(reqs):
|
||||
@@ -1027,7 +1012,7 @@ class SoxClient:
|
||||
self._req_event.clear()
|
||||
|
||||
for req in reqs:
|
||||
self._session.send(req.to_bytes(), send_retry)
|
||||
self._session.send(req.to_bytes())
|
||||
|
||||
deadline = time.monotonic() + 30.0
|
||||
while True:
|
||||
@@ -1051,11 +1036,11 @@ class SoxClient:
|
||||
self._responses = None
|
||||
raise SoxException("Request timeout")
|
||||
|
||||
def _chunk_requests(self, reqs, send_retry=None):
|
||||
def _chunk_requests(self, reqs):
|
||||
results = []
|
||||
for i in range(0, len(reqs), 255):
|
||||
chunk = reqs[i:i + 255]
|
||||
results.extend(self.requests(chunk, send_retry))
|
||||
results.extend(self.requests(chunk))
|
||||
return results
|
||||
|
||||
# called by receiver thread
|
||||
|
||||
Reference in New Issue
Block a user