Add packaged per-camera X4 wake and preview recovery
This commit is contained in:
@@ -30,14 +30,19 @@ class Broker:
|
||||
self.media = None
|
||||
self.media_operations = {}
|
||||
self.capture_locks = {}
|
||||
self.recovery = None
|
||||
|
||||
@staticmethod
|
||||
def validate_identifier(identifier):
|
||||
worker_socket(identifier)
|
||||
|
||||
def capture_lock(self, identifier):
|
||||
with self.media_lock:
|
||||
if identifier not in self.capture_locks and len(self.capture_locks) >= 500:
|
||||
raise RuntimeError("Camera inventory exceeds the protocol limit")
|
||||
return self.capture_locks.setdefault(identifier, threading.Lock())
|
||||
return self.capture_locks.setdefault(identifier, threading.RLock())
|
||||
|
||||
def capture_operation(self, identifier, value):
|
||||
def capture_operation(self, identifier, value, restoring=False):
|
||||
path = worker_socket(identifier)
|
||||
lock = self.capture_lock(identifier)
|
||||
with self.media_lock:
|
||||
@@ -58,6 +63,10 @@ class Broker:
|
||||
key = (identifier, session)
|
||||
starting = value["action_id"] == "preview.start"
|
||||
starting_idle = starting and current.get("status", {}).get("preview") != 1
|
||||
if restoring and (
|
||||
not current.get("online") or current.get("status", {}).get("recording") != 0
|
||||
):
|
||||
return dict(UNKNOWN)
|
||||
if starting:
|
||||
if not current.get("online"):
|
||||
raise RuntimeError("Camera state is unavailable")
|
||||
@@ -67,12 +76,16 @@ class Broker:
|
||||
except (OSError, RuntimeError, ValueError):
|
||||
if starting_idle:
|
||||
self.media.capture(key, path, False)
|
||||
if not starting and self.recovery and not restoring:
|
||||
self.recovery.preview_intent(identifier, value, dict(UNKNOWN))
|
||||
raise
|
||||
if (starting_idle and result.get("state") != "complete") or (
|
||||
not starting and result.get("state") == "complete"
|
||||
):
|
||||
# Releasing a decoder never sends STOP to the camera or SD.
|
||||
self.media.capture(key, path, False)
|
||||
if self.recovery and not restoring:
|
||||
self.recovery.preview_intent(identifier, value, result)
|
||||
return result
|
||||
|
||||
def verify_operation(self, identifier, value):
|
||||
@@ -204,9 +217,21 @@ class Broker:
|
||||
if not re.fullmatch(r"node_[0-9a-f]{64}", node):
|
||||
raise ValueError("Node identity is required")
|
||||
if method == "GET" and route == "/inventory":
|
||||
return {"items": [self.item(item, node) for item in self.snapshots()]}
|
||||
snapshots = self.snapshots()
|
||||
if self.recovery:
|
||||
snapshots = self.recovery.augment(snapshots)
|
||||
return {"items": [self.item(item, node) for item in snapshots]}
|
||||
if method == "POST" and route == "/operation":
|
||||
identifier = value["session"]["device_id"]
|
||||
if value.get("action_id") == "recovery.configure" and self.recovery:
|
||||
return self.recovery.operation(identifier, value)
|
||||
if value.get("action_id") == "preview.stop" and self.recovery:
|
||||
try:
|
||||
current = request(worker_socket(identifier), "/snapshot", timeout=2)
|
||||
except (OSError, RuntimeError, ValueError):
|
||||
current = {}
|
||||
if not current.get("online"):
|
||||
return self.recovery.operation(identifier, value)
|
||||
if value.get("action_id") in ("preview.start", "preview.stop"):
|
||||
return self.capture_operation(identifier, value)
|
||||
if value.get("action_id") in ("offer", "close-peer"):
|
||||
@@ -265,6 +290,11 @@ def main():
|
||||
if path.exists():
|
||||
path.unlink()
|
||||
allowed = {0, pwd.getpwnam("mission-core-node").pw_uid}
|
||||
with Server(path, Broker().dispatch, allowed) as server:
|
||||
broker = Broker()
|
||||
from .recovery import Recovery
|
||||
|
||||
broker.recovery = Recovery(broker, STATE / "recovery")
|
||||
broker.recovery.start()
|
||||
with Server(path, broker.dispatch, allowed) as server:
|
||||
path.chmod(0o660)
|
||||
server.serve_forever(poll_interval=0.5)
|
||||
|
||||
Reference in New Issue
Block a user