remoteRF-server-testing 0.0.2__tar.gz → 0.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/PKG-INFO +1 -1
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/pyproject.toml +1 -1
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/idl/pluto_schema.py +11 -4
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/utils/process_arg.py +2 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/host/host_tunnel_server.py +45 -1
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/server/device_manager.py +77 -3
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/server/rpc_manager.py +9 -2
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server_testing.egg-info/PKG-INFO +1 -1
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/README.md +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/setup.cfg +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/idl/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/idl/device_schema.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/idl/schema.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/utils/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/host/host_auth_token.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/server/grpc_server.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/server/reservation.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/serverrf_cli.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/tools/gist_status.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server_testing.egg-info/SOURCES.txt +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server_testing.egg-info/dependency_links.txt +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server_testing.egg-info/entry_points.txt +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server_testing.egg-info/requires.txt +0 -0
- {remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server_testing.egg-info/top_level.txt +0 -0
|
@@ -84,12 +84,14 @@ class PlutoSchema(DeviceSchema):
|
|
|
84
84
|
self.device.gain_control_mode_chan0 = value
|
|
85
85
|
|
|
86
86
|
@idl_expose(kind="get")
|
|
87
|
-
def
|
|
88
|
-
|
|
87
|
+
def get_hardwaregain_chan0(self):
|
|
88
|
+
"""RX hardware gain (dB). pyadi-iio attribute: hardwaregain_chan0"""
|
|
89
|
+
return self.device.hardwaregain_chan0
|
|
89
90
|
|
|
90
91
|
@idl_expose(kind="set")
|
|
91
|
-
def
|
|
92
|
-
|
|
92
|
+
def set_hardwaregain_chan0(self, value):
|
|
93
|
+
"""RX hardware gain (dB). pyadi-iio attribute: hardwaregain_chan0"""
|
|
94
|
+
self.device.hardwaregain_chan0 = value
|
|
93
95
|
|
|
94
96
|
@idl_expose(kind="get")
|
|
95
97
|
def get_tx_hardwaregain_chan0(self):
|
|
@@ -173,6 +175,11 @@ class PlutoSchema(DeviceSchema):
|
|
|
173
175
|
def call_tx_destroy_buffer(self):
|
|
174
176
|
self.device.tx_destroy_buffer()
|
|
175
177
|
|
|
178
|
+
@idl_expose(kind="call")
|
|
179
|
+
def call_disable_dds(self):
|
|
180
|
+
"""Disable the internal DDS tone generator."""
|
|
181
|
+
self.device.disable_dds()
|
|
182
|
+
|
|
176
183
|
# endregion
|
|
177
184
|
|
|
178
185
|
# region ip / repr
|
|
@@ -31,6 +31,8 @@ def map_arg(value):
|
|
|
31
31
|
arg.string_value = value
|
|
32
32
|
elif isinstance(value, bool):
|
|
33
33
|
arg.bool_value = value
|
|
34
|
+
elif isinstance(value, (list, tuple)):
|
|
35
|
+
return map_arg(np.array(value))
|
|
34
36
|
elif isinstance(value, np.ndarray):
|
|
35
37
|
if np.iscomplexobj(value):
|
|
36
38
|
complex_array = arg.complex_array
|
|
@@ -259,6 +259,12 @@ class HostTunnelRegistry:
|
|
|
259
259
|
self._host_status: Dict[str, HostStatus] = {}
|
|
260
260
|
self._device_status: Dict[str, DeviceStatus] = {}
|
|
261
261
|
|
|
262
|
+
# IDL capability strings received via HostMeta.kv from each host.
|
|
263
|
+
# Keyed by device_id (string gid). Value is the compact caps string
|
|
264
|
+
# emitted by the host's capabilities_string() helper, e.g.:
|
|
265
|
+
# "disable_dds:call0,filter:rw,hardwaregain_chan0:rw,..."
|
|
266
|
+
self._host_device_caps: Dict[str, str] = {}
|
|
267
|
+
|
|
262
268
|
# persistence
|
|
263
269
|
cfg = cfg_dir_from_file(__file__)
|
|
264
270
|
cfg.mkdir(parents=True, exist_ok=True)
|
|
@@ -531,6 +537,26 @@ class HostTunnelRegistry:
|
|
|
531
537
|
|
|
532
538
|
info(f"[registry] heartbeat: host_id={host_id!r} unix_ms={now}")
|
|
533
539
|
|
|
540
|
+
# ── IDL capability cache ────────────────────────────────────────
|
|
541
|
+
|
|
542
|
+
def store_host_device_caps(self, host_id: str, device_id: str, caps: str) -> None:
|
|
543
|
+
"""
|
|
544
|
+
Store the IDL capability string for a host device.
|
|
545
|
+
|
|
546
|
+
Called when the server receives HostMeta.kv["device.<gid>.caps"] in a
|
|
547
|
+
MetaResponse. The caps string is the compact attr:access list emitted
|
|
548
|
+
by the host's capabilities_string() helper.
|
|
549
|
+
"""
|
|
550
|
+
with self._lock:
|
|
551
|
+
self._host_device_caps[str(device_id)] = str(caps)
|
|
552
|
+
|
|
553
|
+
def get_host_device_caps(self, device_id: str) -> str:
|
|
554
|
+
"""
|
|
555
|
+
Return the IDL capability string for *device_id*, or "" if not yet received.
|
|
556
|
+
"""
|
|
557
|
+
with self._lock:
|
|
558
|
+
return self._host_device_caps.get(str(device_id), "")
|
|
559
|
+
|
|
534
560
|
def announce_devices(self, host_id: str, devices: list) -> None:
|
|
535
561
|
info(f"[registry] announce_devices: host_id={host_id!r} n={len(devices)}")
|
|
536
562
|
|
|
@@ -1147,7 +1173,7 @@ class HostTunnelServicer(host_tunnel_pb2_grpc.HostTunnelServicer):
|
|
|
1147
1173
|
warn(f"[inbound] rpc_response has no inflight waiter req_id={rid} stream_sess={stream_uuid}")
|
|
1148
1174
|
|
|
1149
1175
|
elif which == "meta_response":
|
|
1150
|
-
# NEW proto: MetaResponse.meta.devices
|
|
1176
|
+
# NEW proto: MetaResponse.meta.devices + meta.kv (IDL caps)
|
|
1151
1177
|
if host_id is None or session is None:
|
|
1152
1178
|
warn(f"[inbound] meta_response before hello/session: ignoring peer={peer} stream_sess={stream_uuid}")
|
|
1153
1179
|
continue
|
|
@@ -1186,6 +1212,24 @@ class HostTunnelServicer(host_tunnel_pb2_grpc.HostTunnelServicer):
|
|
|
1186
1212
|
else:
|
|
1187
1213
|
info("[inbound] META_RESPONSE had no meta.devices (or empty)")
|
|
1188
1214
|
|
|
1215
|
+
# Read per-device IDL capability strings from meta.kv.
|
|
1216
|
+
# Keys: "device.<gid>.caps" "device.<gid>.type"
|
|
1217
|
+
# These are emitted by the host's tunnel_agent._collect_meta().
|
|
1218
|
+
try:
|
|
1219
|
+
kv = dict(getattr(meta, "kv", {}) or {})
|
|
1220
|
+
for k, v in kv.items():
|
|
1221
|
+
# e.g. k="device.0.caps", v="disable_dds:call0,filter:rw,..."
|
|
1222
|
+
parts = k.split(".")
|
|
1223
|
+
if len(parts) == 3 and parts[0] == "device" and parts[2] == "caps":
|
|
1224
|
+
device_id = parts[1]
|
|
1225
|
+
self.registry.store_host_device_caps(host_id, device_id, str(v))
|
|
1226
|
+
info(
|
|
1227
|
+
f"[inbound] stored IDL caps device_id={device_id} "
|
|
1228
|
+
f"host_id={host_id!r} n_attrs={len(v.split(','))}"
|
|
1229
|
+
)
|
|
1230
|
+
except Exception:
|
|
1231
|
+
exception("[inbound] META_RESPONSE failed reading meta.kv caps")
|
|
1232
|
+
|
|
1189
1233
|
elif which == "heartbeat":
|
|
1190
1234
|
if session is not None and host_id is not None:
|
|
1191
1235
|
ms = int(getattr(frame.heartbeat, "unix_ms", 0) or 0) or now_ms()
|
|
@@ -491,11 +491,28 @@ def get_device_by_id(device_id: int):
|
|
|
491
491
|
return (st.dev, st.salt, st.hsh)
|
|
492
492
|
|
|
493
493
|
def get_device_schema(device_id: int):
|
|
494
|
-
"""
|
|
494
|
+
"""
|
|
495
|
+
Return a DeviceSchema instance for this device, or None if unavailable.
|
|
496
|
+
|
|
497
|
+
For local devices: returns the live bound schema instance.
|
|
498
|
+
For host devices: instantiates an unbound schema from the driver registry
|
|
499
|
+
using the device's dtype — sufficient for IDL/hash purposes since the
|
|
500
|
+
schema structure is defined by the class, not the live device.
|
|
501
|
+
"""
|
|
495
502
|
with _state_lock:
|
|
496
503
|
st = _devices.get(int(device_id))
|
|
497
|
-
if st
|
|
498
|
-
return
|
|
504
|
+
if st is None:
|
|
505
|
+
return None
|
|
506
|
+
|
|
507
|
+
if st.origin == "local" and st.dev is not None:
|
|
508
|
+
return st.dev
|
|
509
|
+
|
|
510
|
+
if st.origin == "host" and st.dtype:
|
|
511
|
+
try:
|
|
512
|
+
return get_driver_class(st.dtype)()
|
|
513
|
+
except KeyError:
|
|
514
|
+
return None
|
|
515
|
+
|
|
499
516
|
return None
|
|
500
517
|
|
|
501
518
|
def get_schema_by_token(api_token: str):
|
|
@@ -542,6 +559,63 @@ def get_schema_by_token(api_token: str):
|
|
|
542
559
|
return None
|
|
543
560
|
return get_device_schema(gid)
|
|
544
561
|
|
|
562
|
+
def resolve_gid_for_token(api_token: str) -> Optional[int]:
|
|
563
|
+
"""
|
|
564
|
+
Resolve an API token to its device gid regardless of device origin
|
|
565
|
+
(local or host/VirtualDevice).
|
|
566
|
+
|
|
567
|
+
Unlike get_schema_by_token — which returns None for host devices because
|
|
568
|
+
they have no salt/hash — this function also covers:
|
|
569
|
+
• parsed master-tokens ('{gid}:secret') → any device by gid
|
|
570
|
+
• raw master_token → first connected device
|
|
571
|
+
• regular per-device tokens (salt/hsh) → local devices
|
|
572
|
+
|
|
573
|
+
Used by IDL queries that need a schema even for host devices.
|
|
574
|
+
Does NOT acquire any device I/O lock.
|
|
575
|
+
"""
|
|
576
|
+
if not api_token:
|
|
577
|
+
return None
|
|
578
|
+
|
|
579
|
+
_sync_host_devices()
|
|
580
|
+
|
|
581
|
+
# Parsed master-token: resolves any device directly by gid (local or host).
|
|
582
|
+
parsed = parse_mastertoken(api_token)
|
|
583
|
+
if parsed:
|
|
584
|
+
cand, _ = parsed
|
|
585
|
+
with _state_lock:
|
|
586
|
+
st = _devices.get(int(cand))
|
|
587
|
+
if st and _is_connected(st):
|
|
588
|
+
return int(cand)
|
|
589
|
+
return None
|
|
590
|
+
|
|
591
|
+
# Raw master token — prefer local, fall back to any connected device.
|
|
592
|
+
if api_token == master_token:
|
|
593
|
+
with _state_lock:
|
|
594
|
+
# local first
|
|
595
|
+
for k, st in _devices.items():
|
|
596
|
+
if st.origin == "local" and _is_connected(st) and st.salt == "" and st.hsh == "":
|
|
597
|
+
return k
|
|
598
|
+
# then host
|
|
599
|
+
for k, st in _devices.items():
|
|
600
|
+
if _is_connected(st) and st.salt == "" and st.hsh == "":
|
|
601
|
+
return k
|
|
602
|
+
return None
|
|
603
|
+
|
|
604
|
+
# Regular per-device token (salt/hsh). Host devices have empty salt/hsh
|
|
605
|
+
# so they won't match here — but this covers local devices correctly.
|
|
606
|
+
with _state_lock:
|
|
607
|
+
snapshot = [
|
|
608
|
+
(k, st.salt, st.hsh)
|
|
609
|
+
for k, st in _devices.items()
|
|
610
|
+
if _is_connected(st)
|
|
611
|
+
]
|
|
612
|
+
for k, salt, hsh in snapshot:
|
|
613
|
+
if salt and hsh and validate_token(salt, hsh, api_token):
|
|
614
|
+
return k
|
|
615
|
+
|
|
616
|
+
return None
|
|
617
|
+
|
|
618
|
+
|
|
545
619
|
def find_device_id_by_name(name: str) -> Optional[int]:
|
|
546
620
|
"""Return the gid of the first device whose name matches (case-insensitive), or None."""
|
|
547
621
|
name_lower = name.strip().lower()
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
from .reservation import reservation_handler
|
|
54
54
|
from ..common.utils import *
|
|
55
|
-
from .device_manager import acquire_device, VirtualDevice, get_device_schema, find_device_id_by_name, get_schema_by_token
|
|
55
|
+
from .device_manager import acquire_device, VirtualDevice, get_device_schema, find_device_id_by_name, get_schema_by_token, resolve_gid_for_token
|
|
56
56
|
from ..host import host_tunnel_server as hts
|
|
57
57
|
|
|
58
58
|
class RpcManager:
|
|
@@ -151,7 +151,14 @@ class RpcManager:
|
|
|
151
151
|
api_token = str(unmap_arg(args['token']))
|
|
152
152
|
schema = get_schema_by_token(api_token)
|
|
153
153
|
if schema is None:
|
|
154
|
-
|
|
154
|
+
# get_schema_by_token returns None for host (VirtualDevice) entries
|
|
155
|
+
# because they carry no salt/hsh. Fall through via gid resolution
|
|
156
|
+
# which uses get_device_schema and handles host devices correctly.
|
|
157
|
+
gid = resolve_gid_for_token(api_token)
|
|
158
|
+
if gid is not None:
|
|
159
|
+
schema = get_device_schema(gid)
|
|
160
|
+
if schema is None:
|
|
161
|
+
return {'error': map_arg('No schema available for this token (invalid token or device offline)')}
|
|
155
162
|
|
|
156
163
|
elif 'device_id' in args:
|
|
157
164
|
try:
|
|
File without changes
|
|
File without changes
|
{remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/host/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server_testing-0.0.2 → remoterf_server_testing-0.0.4}/src/remoteRF_server/serverrf_cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|