remoteRF-server 1.1.3__tar.gz → 1.4.2__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-1.1.3 → remoterf_server-1.4.2}/PKG-INFO +3 -4
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/README.md +1 -2
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/pyproject.toml +2 -2
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/federation.proto +3 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/federation_pb2.py +6 -6
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/grpc_host.proto +1 -0
- remoterf_server-1.4.2/src/remoteRF_server/common/grpc/grpc_host_pb2.py +78 -0
- {remoterf_server-1.1.3/src/remoteRF_server/drivers/adalm_pluto → remoterf_server-1.4.2/src/remoteRF_server/common/idl}/pluto_schema.py +23 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/__init__.py +9 -1
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/process_arg.py +8 -6
- {remoterf_server-1.1.3/src/remoteRF_server/common/idl → remoterf_server-1.4.2/src/remoteRF_server/drivers/adalm_pluto}/pluto_schema.py +23 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/adalm_pluto/poe.py +23 -11
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/rtl_sdr/rtl_sdr_schema.py +12 -3
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/usrp/usrp_schema.py +55 -3
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/host/host_directory_store.py +25 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/host/host_tunnel_server.py +55 -2
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/acc_perms.py +5 -1
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/commands.py +10 -9
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/device_manager.py +196 -79
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/direct_path.py +23 -51
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/dynamic_v2.py +9 -2
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/cli.py +26 -51
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/cluster.py +149 -109
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/destination.py +142 -66
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/devices.py +87 -38
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/enrollment.py +47 -16
- remoterf_server-1.4.2/src/remoteRF_server/server/federation/global_home.py +81 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/home.py +184 -48
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/home_devices.py +27 -18
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/migrations.py +3 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/global_onboarding.py +14 -1
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/grpc_server.py +39 -29
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/reservation.py +139 -35
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/rpc_manager.py +104 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/user_group_cli.py +109 -15
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/user_group_handler.py +88 -23
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/serverrf_cli.py +97 -18
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server.egg-info/PKG-INFO +3 -4
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server.egg-info/SOURCES.txt +13 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server.egg-info/requires.txt +1 -1
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server.egg-info/top_level.txt +0 -1
- remoterf_server-1.4.2/tests/test_account_names.py +59 -0
- remoterf_server-1.4.2/tests/test_account_removal.py +52 -0
- remoterf_server-1.4.2/tests/test_activity_log.py +26 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_cluster.py +70 -227
- remoterf_server-1.4.2/tests/test_contrib_drivers.py +238 -0
- remoterf_server-1.4.2/tests/test_detached_device_rpc.py +447 -0
- remoterf_server-1.4.2/tests/test_device_handles.py +460 -0
- remoterf_server-1.4.2/tests/test_device_init.py +123 -0
- remoterf_server-1.4.2/tests/test_devices_list.py +22 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_direct_fixed_port.py +25 -3
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_direct_path_server.py +78 -49
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_dynamic_v2.py +80 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_federated_direct_path.py +72 -3
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_gate_e_federation.py +32 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_gate_e_proto.py +4 -1
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_gate_f_enrollment.py +40 -5
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_gate_h_devices.py +96 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_gate_i_direct_path.py +7 -3
- remoterf_server-1.4.2/tests/test_global_home.py +144 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_pluto_compat.py +62 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_pluto_poe.py +48 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_process_arg.py +11 -0
- remoterf_server-1.4.2/tests/test_reservation_handoff.py +127 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_reservation_policy_b.py +10 -0
- remoterf_server-1.4.2/tests/test_rpc_manager_batch.py +156 -0
- remoterf_server-1.4.2/tests/test_rpc_manager_rx_n.py +93 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_rtl_sdr_schema.py +47 -2
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_user_group_cli.py +56 -1
- remoterf_server-1.4.2/tests/test_usrp_preload.py +84 -0
- remoterf_server-1.1.3/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -78
- remoterf_server-1.1.3/tests/test_reservation_handoff.py +0 -70
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/LICENSE +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/setup.cfg +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/deployment_capabilities.proto +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/deployment_capabilities_pb2.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/deployment_capabilities_pb2_grpc.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/federation_pb2_grpc.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/grpc.proto +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/v2_codec.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/idl/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/idl/device_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/idl/pluto_mimo_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/idl/schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/idl/v2.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/db_location.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/panel.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/adalm_pluto/pluto_mimo_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/hackrf/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/hackrf/hackrf_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/rtl_sdr/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/ti_mmwave/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/ti_mmwave/ti_mmwave_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/usrp/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/usrp/policy.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/usrp/profiles.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/usrp/uhd_manifest.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/usrp_phaser/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/usrp_phaser/usrp_phaser_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/host/host_auth_token.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/console.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/control.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/deployment/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/deployment/capabilities.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/dynamic_v2_bridge.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/admin.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/contracts.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/identity.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/legacy_state.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/peers.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/permissions.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/principals.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/runtime.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/federation/trust.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/host_dynamic_v2.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/server/v2_errors.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/tools/gist_status.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/tools/introspect_uhd.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_account_hardening.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_bind_host.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_cert_provider.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_client_sessions.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_commands.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_console.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_default_rtl_sdr_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_default_usrp_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_deployment_capabilities.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_federated_dynamic_v2.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_federation_edges.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_first_run.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_gate_d_contracts.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_gate_g1_logging.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_gate_g_permissions.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_gen_certs.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_global_accounts_migration.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_global_onboarding.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_global_v1_disposal.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_hackrf_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_home_device_plane.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_host_directory_store.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_host_dynamic_v2.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_idl_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_panel.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_peer_transport.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_reservation_timestamps.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_rpc_manager_calln.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_soak_federation.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_status_push.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_ti_mmwave_hardware.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_ti_mmwave_schema.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_usrp_b205mini_uhd410_hardware.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_usrp_client_server_hardware.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_usrp_client_server_localhost.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_usrp_n210_client_server_hardware.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_usrp_profiles.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_usrp_remoterf_rx_hardware.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_usrp_uhd410_hardware.py +0 -0
- {remoterf_server-1.1.3 → remoterf_server-1.4.2}/tests/test_wipe.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoteRF-server
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.2
|
|
4
4
|
Summary: RemoteRF server-side control package
|
|
5
5
|
License: GPL-3.0-or-later
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -9,7 +9,7 @@ Classifier: Operating System :: OS Independent
|
|
|
9
9
|
Requires-Python: >=3.10
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist: remoterf-federation-core==0.
|
|
12
|
+
Requires-Dist: remoterf-federation-core==0.21.0
|
|
13
13
|
Requires-Dist: grpcio<2.0.0,>=1.81.1
|
|
14
14
|
Requires-Dist: protobuf<7.0.0,>=6.33.5
|
|
15
15
|
Requires-Dist: numpy
|
|
@@ -890,8 +890,7 @@ After running `serverrf --serve`, you’ll enter the **RemoteRF Server Shell**.
|
|
|
890
890
|
|
|
891
891
|
### Devices
|
|
892
892
|
|
|
893
|
-
* `devices list` — List all devices
|
|
894
|
-
* `devices status` — Show live device routing/online/last_seen from the Host Tunnel
|
|
893
|
+
* `devices list` — List all devices, with the host each is on (`local` for this server's own) and when it was last seen
|
|
895
894
|
|
|
896
895
|
---
|
|
897
896
|
|
|
@@ -859,8 +859,7 @@ After running `serverrf --serve`, you’ll enter the **RemoteRF Server Shell**.
|
|
|
859
859
|
|
|
860
860
|
### Devices
|
|
861
861
|
|
|
862
|
-
* `devices list` — List all devices
|
|
863
|
-
* `devices status` — Show live device routing/online/last_seen from the Host Tunnel
|
|
862
|
+
* `devices list` — List all devices, with the host each is on (`local` for this server's own) and when it was last seen
|
|
864
863
|
|
|
865
864
|
---
|
|
866
865
|
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "remoteRF-server"
|
|
7
|
-
version = "1.
|
|
7
|
+
version = "1.4.2"
|
|
8
8
|
description = "RemoteRF server-side control package"
|
|
9
9
|
license = { text = "GPL-3.0-or-later" }
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"remoterf-federation-core==0.
|
|
12
|
+
"remoterf-federation-core==0.21.0",
|
|
13
13
|
"grpcio>=1.81.1,<2.0.0",
|
|
14
14
|
"protobuf>=6.33.5,<7.0.0",
|
|
15
15
|
"numpy",
|
{remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/federation.proto
RENAMED
|
@@ -11,10 +11,13 @@ message FederationRequest {
|
|
|
11
11
|
string protocol_version = 1;
|
|
12
12
|
string grant = 2;
|
|
13
13
|
string request_json = 3;
|
|
14
|
+
bytes frame = 4; // raw GenericRPCRequest when request_json is detached; else empty
|
|
15
|
+
bool accept_detached = 5; // requester can take a detached result + frame
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
message FederationResponse {
|
|
17
19
|
string protocol_version = 1;
|
|
18
20
|
string signed_result = 2;
|
|
19
21
|
ErrorEnvelope error = 3;
|
|
22
|
+
bytes frame = 4; // raw GenericRPCResponse when signed_result is detached; else empty
|
|
20
23
|
}
|
{remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/grpc/federation_pb2.py
RENAMED
|
@@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default()
|
|
|
25
25
|
from . import grpc_pb2 as grpc__pb2
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x66\x65\x64\x65ration.proto\x12\tremote_rf\x1a\ngrpc.proto\"
|
|
28
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x66\x65\x64\x65ration.proto\x12\tremote_rf\x1a\ngrpc.proto\"z\n\x11\x46\x65\x64\x65rationRequest\x12\x18\n\x10protocol_version\x18\x01 \x01(\t\x12\r\n\x05grant\x18\x02 \x01(\t\x12\x14\n\x0crequest_json\x18\x03 \x01(\t\x12\r\n\x05\x66rame\x18\x04 \x01(\x0c\x12\x17\n\x0f\x61\x63\x63\x65pt_detached\x18\x05 \x01(\x08\"}\n\x12\x46\x65\x64\x65rationResponse\x12\x18\n\x10protocol_version\x18\x01 \x01(\t\x12\x15\n\rsigned_result\x18\x02 \x01(\t\x12\'\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x18.remote_rf.ErrorEnvelope\x12\r\n\x05\x66rame\x18\x04 \x01(\x0c\x32V\n\x0c\x46\x65\x64\x65rationV2\x12\x46\n\x07\x45xecute\x12\x1c.remote_rf.FederationRequest\x1a\x1d.remote_rf.FederationResponseb\x06proto3')
|
|
29
29
|
|
|
30
30
|
_globals = globals()
|
|
31
31
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -33,9 +33,9 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'federation_pb2', _globals)
|
|
|
33
33
|
if not _descriptor._USE_C_DESCRIPTORS:
|
|
34
34
|
DESCRIPTOR._loaded_options = None
|
|
35
35
|
_globals['_FEDERATIONREQUEST']._serialized_start=43
|
|
36
|
-
_globals['_FEDERATIONREQUEST']._serialized_end=
|
|
37
|
-
_globals['_FEDERATIONRESPONSE']._serialized_start=
|
|
38
|
-
_globals['_FEDERATIONRESPONSE']._serialized_end=
|
|
39
|
-
_globals['_FEDERATIONV2']._serialized_start=
|
|
40
|
-
_globals['_FEDERATIONV2']._serialized_end=
|
|
36
|
+
_globals['_FEDERATIONREQUEST']._serialized_end=165
|
|
37
|
+
_globals['_FEDERATIONRESPONSE']._serialized_start=167
|
|
38
|
+
_globals['_FEDERATIONRESPONSE']._serialized_end=292
|
|
39
|
+
_globals['_FEDERATIONV2']._serialized_start=294
|
|
40
|
+
_globals['_FEDERATIONV2']._serialized_end=380
|
|
41
41
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Copyright (C) 2026 RemoteRF
|
|
2
|
+
#
|
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
6
|
+
# (at your option) any later version.
|
|
7
|
+
#
|
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11
|
+
# GNU General Public License for more details.
|
|
12
|
+
#
|
|
13
|
+
# You should have received a copy of the GNU General Public License
|
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
15
|
+
|
|
16
|
+
# -*- coding: utf-8 -*-
|
|
17
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
18
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
19
|
+
# source: grpc_host.proto
|
|
20
|
+
# Protobuf Python Version: 6.31.1
|
|
21
|
+
"""Generated protocol buffer code."""
|
|
22
|
+
from google.protobuf import descriptor as _descriptor
|
|
23
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
24
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
25
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
26
|
+
from google.protobuf.internal import builder as _builder
|
|
27
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
28
|
+
_runtime_version.Domain.PUBLIC,
|
|
29
|
+
6,
|
|
30
|
+
31,
|
|
31
|
+
1,
|
|
32
|
+
'',
|
|
33
|
+
'grpc_host.proto'
|
|
34
|
+
)
|
|
35
|
+
# @@protoc_insertion_point(imports)
|
|
36
|
+
|
|
37
|
+
_sym_db = _symbol_database.Default()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
from . import grpc_pb2 as grpc__pb2
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0fgrpc_host.proto\x12\x08remoterf\x1a\ngrpc.proto\"A\n\tHostHello\x12\x0f\n\x07host_id\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x12\n\nhost_token\x18\x03 \x01(\t\"n\n\nDeviceInfo\x12\x10\n\x08local_id\x18\x01 \x01(\r\x12\r\n\x05label\x18\x02 \x01(\t\x12\x11\n\tdevice_id\x18\x03 \x01(\t\x12\x0e\n\x06serial\x18\x04 \x01(\t\x12\x0c\n\x04kind\x18\x05 \x01(\t\x12\x0e\n\x06handle\x18\x06 \x01(\t\"_\n\x0e\x44\x65viceAnnounce\x12%\n\x07\x64\x65vices\x18\x01 \x03(\x0b\x32\x14.remoterf.DeviceInfo\x12\x0f\n\x07unix_ms\x18\x02 \x01(\x04\x12\x15\n\rfull_snapshot\x18\x03 \x01(\x08\"\xab\x01\n\nRpcRequest\x12\x0e\n\x06req_id\x18\x01 \x01(\t\x12\x18\n\x10global_device_id\x18\x02 \x01(\r\x12\x17\n\x0flocal_device_id\x18\x03 \x01(\r\x12\x11\n\tdevice_id\x18\x04 \x01(\t\x12-\n\x07request\x18\x05 \x01(\x0b\x32\x1c.remote_rf.GenericRPCRequest\x12\x18\n\x10\x64\x65\x61\x64line_unix_ms\x18\x06 \x01(\x04\"i\n\x0bRpcResponse\x12\x0e\n\x06req_id\x18\x01 \x01(\t\x12\n\n\x02ok\x18\x02 \x01(\x08\x12/\n\x08response\x18\x03 \x01(\x0b\x32\x1d.remote_rf.GenericRPCResponse\x12\r\n\x05\x65rror\x18\x04 \x01(\t\"\x1c\n\tHeartbeat\x12\x0f\n\x07unix_ms\x18\x01 \x01(\x04\"\x18\n\x06\x43\x61ncel\x12\x0e\n\x06req_id\x18\x01 \x01(\t\"e\n\x0bMetaRequest\x12\x0e\n\x06req_id\x18\x01 \x01(\t\x12\x18\n\x10include_platform\x18\x02 \x01(\x08\x12\x13\n\x0binclude_env\x18\x03 \x01(\x08\x12\x17\n\x0finclude_devices\x18\x04 \x01(\x08\"\xb7\x01\n\x08HostMeta\x12\x0f\n\x07host_id\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x0f\n\x07unix_ms\x18\x03 \x01(\x04\x12&\n\x02kv\x18\x04 \x03(\x0b\x32\x1a.remoterf.HostMeta.KvEntry\x12%\n\x07\x64\x65vices\x18\x05 \x03(\x0b\x32\x14.remoterf.DeviceInfo\x1a)\n\x07KvEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"[\n\x0cMetaResponse\x12\x0e\n\x06req_id\x18\x01 \x01(\t\x12\n\n\x02ok\x18\x02 \x01(\x08\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12 \n\x04meta\x18\x04 \x01(\x0b\x32\x12.remoterf.HostMeta\"\xf7\x02\n\tHostFrame\x12$\n\x05hello\x18\x01 \x01(\x0b\x32\x13.remoterf.HostHelloH\x00\x12\x33\n\x0f\x64\x65vice_announce\x18\x02 \x01(\x0b\x32\x18.remoterf.DeviceAnnounceH\x00\x12+\n\x0brpc_request\x18\x03 \x01(\x0b\x32\x14.remoterf.RpcRequestH\x00\x12-\n\x0crpc_response\x18\x04 \x01(\x0b\x32\x15.remoterf.RpcResponseH\x00\x12(\n\theartbeat\x18\x05 \x01(\x0b\x32\x13.remoterf.HeartbeatH\x00\x12\"\n\x06\x63\x61ncel\x18\x06 \x01(\x0b\x32\x10.remoterf.CancelH\x00\x12-\n\x0cmeta_request\x18\x07 \x01(\x0b\x32\x15.remoterf.MetaRequestH\x00\x12/\n\rmeta_response\x18\x08 \x01(\x0b\x32\x16.remoterf.MetaResponseH\x00\x42\x05\n\x03msg2E\n\nHostTunnel\x12\x37\n\x07\x43onnect\x12\x13.remoterf.HostFrame\x1a\x13.remoterf.HostFrame(\x01\x30\x01\x62\x06proto3')
|
|
44
|
+
|
|
45
|
+
_globals = globals()
|
|
46
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
47
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'grpc_host_pb2', _globals)
|
|
48
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
49
|
+
DESCRIPTOR._loaded_options = None
|
|
50
|
+
_globals['_HOSTMETA_KVENTRY']._loaded_options = None
|
|
51
|
+
_globals['_HOSTMETA_KVENTRY']._serialized_options = b'8\001'
|
|
52
|
+
_globals['_HOSTHELLO']._serialized_start=41
|
|
53
|
+
_globals['_HOSTHELLO']._serialized_end=106
|
|
54
|
+
_globals['_DEVICEINFO']._serialized_start=108
|
|
55
|
+
_globals['_DEVICEINFO']._serialized_end=218
|
|
56
|
+
_globals['_DEVICEANNOUNCE']._serialized_start=220
|
|
57
|
+
_globals['_DEVICEANNOUNCE']._serialized_end=315
|
|
58
|
+
_globals['_RPCREQUEST']._serialized_start=318
|
|
59
|
+
_globals['_RPCREQUEST']._serialized_end=489
|
|
60
|
+
_globals['_RPCRESPONSE']._serialized_start=491
|
|
61
|
+
_globals['_RPCRESPONSE']._serialized_end=596
|
|
62
|
+
_globals['_HEARTBEAT']._serialized_start=598
|
|
63
|
+
_globals['_HEARTBEAT']._serialized_end=626
|
|
64
|
+
_globals['_CANCEL']._serialized_start=628
|
|
65
|
+
_globals['_CANCEL']._serialized_end=652
|
|
66
|
+
_globals['_METAREQUEST']._serialized_start=654
|
|
67
|
+
_globals['_METAREQUEST']._serialized_end=755
|
|
68
|
+
_globals['_HOSTMETA']._serialized_start=758
|
|
69
|
+
_globals['_HOSTMETA']._serialized_end=941
|
|
70
|
+
_globals['_HOSTMETA_KVENTRY']._serialized_start=900
|
|
71
|
+
_globals['_HOSTMETA_KVENTRY']._serialized_end=941
|
|
72
|
+
_globals['_METARESPONSE']._serialized_start=943
|
|
73
|
+
_globals['_METARESPONSE']._serialized_end=1034
|
|
74
|
+
_globals['_HOSTFRAME']._serialized_start=1037
|
|
75
|
+
_globals['_HOSTFRAME']._serialized_end=1412
|
|
76
|
+
_globals['_HOSTTUNNEL']._serialized_start=1414
|
|
77
|
+
_globals['_HOSTTUNNEL']._serialized_end=1483
|
|
78
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -89,6 +89,7 @@ class PlutoSchema(DeviceSchema):
|
|
|
89
89
|
device_type = "adalm_pluto"
|
|
90
90
|
client_class = "Pluto"
|
|
91
91
|
driver_version = "0.0.3"
|
|
92
|
+
_fir_rate = None # the rate whose pyadi FIR this schema last loaded
|
|
92
93
|
|
|
93
94
|
@staticmethod
|
|
94
95
|
def make_device(**kwargs):
|
|
@@ -106,6 +107,7 @@ class PlutoSchema(DeviceSchema):
|
|
|
106
107
|
|
|
107
108
|
@idl_expose(kind="set")
|
|
108
109
|
def set_filter(self, value):
|
|
110
|
+
self._fir_rate = None
|
|
109
111
|
self.device.filter = value
|
|
110
112
|
|
|
111
113
|
@idl_expose(kind="get")
|
|
@@ -164,7 +166,20 @@ class PlutoSchema(DeviceSchema):
|
|
|
164
166
|
|
|
165
167
|
@idl_expose(kind="set")
|
|
166
168
|
def set_sample_rate(self, value):
|
|
169
|
+
# pyadi reloads its FIR and re-runs the AD9361 rate chain (~0.8 s) even
|
|
170
|
+
# for an unchanged rate: skip only what our own last set left in place.
|
|
171
|
+
if value == self._fir_rate:
|
|
172
|
+
try:
|
|
173
|
+
if (
|
|
174
|
+
self.device.sample_rate == value
|
|
175
|
+
and self.device._get_iio_attr("out", "voltage_filter_fir_en", False) == 1
|
|
176
|
+
):
|
|
177
|
+
return
|
|
178
|
+
except Exception:
|
|
179
|
+
pass
|
|
180
|
+
self._fir_rate = None
|
|
167
181
|
self.device.sample_rate = value
|
|
182
|
+
self._fir_rate = value
|
|
168
183
|
|
|
169
184
|
@idl_expose(kind="get")
|
|
170
185
|
def get_rx_lo(self):
|
|
@@ -222,12 +237,18 @@ class PlutoSchema(DeviceSchema):
|
|
|
222
237
|
def reset_for_next_holder(self):
|
|
223
238
|
"""A reservation ended: a PoE Pluto powers off. Otherwise stop its cyclic
|
|
224
239
|
transmission, and drop its RX buffer so the next holder's rx_buffer_size takes effect."""
|
|
240
|
+
self._fir_rate = None
|
|
225
241
|
if isinstance(self.device, PoePluto):
|
|
226
242
|
self.device.power_down()
|
|
227
243
|
elif self.device is not None:
|
|
228
244
|
self.device.tx_destroy_buffer()
|
|
229
245
|
self.device.rx_destroy_buffer()
|
|
230
246
|
|
|
247
|
+
def prepare_for_holder(self):
|
|
248
|
+
"""A reservation started: a PoE Pluto boots now, not at the holder's first command."""
|
|
249
|
+
if isinstance(self.device, PoePluto):
|
|
250
|
+
self.device.power_up()
|
|
251
|
+
|
|
231
252
|
@idl_expose(kind="call")
|
|
232
253
|
def call_disable_dds(self):
|
|
233
254
|
"""Disable the internal DDS tone generator."""
|
|
@@ -244,12 +265,14 @@ class PlutoSchema(DeviceSchema):
|
|
|
244
265
|
@idl_expose(kind="call")
|
|
245
266
|
def call_power_down(self):
|
|
246
267
|
"""PoE Plutos: cut power; the next command powers it back up. No-op on other Plutos."""
|
|
268
|
+
self._fir_rate = None
|
|
247
269
|
if isinstance(self.device, PoePluto):
|
|
248
270
|
self.device.power_down()
|
|
249
271
|
|
|
250
272
|
@idl_expose(kind="call")
|
|
251
273
|
def call_power_cycle(self):
|
|
252
274
|
"""PoE Plutos: power off, then on, and wait for boot. No-op on other Plutos."""
|
|
275
|
+
self._fir_rate = None
|
|
253
276
|
if isinstance(self.device, PoePluto):
|
|
254
277
|
self.device.power_cycle()
|
|
255
278
|
|
{remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/__init__.py
RENAMED
|
@@ -13,9 +13,17 @@
|
|
|
13
13
|
# You should have received a copy of the GNU General Public License
|
|
14
14
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
15
15
|
|
|
16
|
+
import re
|
|
17
|
+
|
|
16
18
|
from .api_token import validate_token, generate_token, hash_token
|
|
17
19
|
from .process_arg import unmap_arg, map_arg
|
|
18
20
|
from .ansi_codes import printf, stylize, Sty
|
|
19
21
|
from .list_string import list_to_str, str_to_list
|
|
20
22
|
from .db_connection import db_connection
|
|
21
|
-
from .db_location import get_db_dir, get_certs_dir, get_drivers_dir, get_remoterf_root
|
|
23
|
+
from .db_location import get_db_dir, get_certs_dir, get_drivers_dir, get_remoterf_root
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def device_name_key(name: str) -> str:
|
|
27
|
+
"""A device name as typed and matched: whitespace and '_' are interchangeable,
|
|
28
|
+
runs collapse and the ends are ignored ('pluto A' == 'pluto_A'). Case is kept."""
|
|
29
|
+
return re.sub(r"[\s_]+", "_", name).strip("_")
|
{remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/common/utils/process_arg.py
RENAMED
|
@@ -98,7 +98,7 @@ def _json_safe(value: Any):
|
|
|
98
98
|
return _json_safe(value.as_payload())
|
|
99
99
|
if isinstance(value, np.ndarray):
|
|
100
100
|
if value.dtype == object:
|
|
101
|
-
|
|
101
|
+
return _json_safe(value.tolist())
|
|
102
102
|
array = np.asarray(value)
|
|
103
103
|
if np.iscomplexobj(array):
|
|
104
104
|
data = [[float(num.real), float(num.imag)] for num in array.ravel()]
|
|
@@ -206,13 +206,15 @@ def map_arg(value: Any) -> grpc_pb2.Argument:
|
|
|
206
206
|
try:
|
|
207
207
|
array = np.stack(value)
|
|
208
208
|
except Exception:
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
pass
|
|
210
|
+
if array.dtype == object:
|
|
211
|
+
arg.json_value = json.dumps(_json_safe(value), separators=(",", ":"))
|
|
212
|
+
return arg
|
|
211
213
|
return map_arg(array)
|
|
212
214
|
elif isinstance(value, np.ndarray):
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if value.dtype.kind not in _SUPPORTED_NDARRAY_KINDS:
|
|
215
|
+
# Object arrays (a list of dicts, say) and non-numeric dtypes go as
|
|
216
|
+
# JSON, which the client decodes back to lists.
|
|
217
|
+
if value.dtype == object or value.dtype.kind not in _SUPPORTED_NDARRAY_KINDS:
|
|
216
218
|
arg.json_value = json.dumps(
|
|
217
219
|
_json_safe(value.tolist()), separators=(",", ":")
|
|
218
220
|
)
|
|
@@ -89,6 +89,7 @@ class PlutoSchema(DeviceSchema):
|
|
|
89
89
|
device_type = "adalm_pluto"
|
|
90
90
|
client_class = "Pluto"
|
|
91
91
|
driver_version = "0.0.3"
|
|
92
|
+
_fir_rate = None # the rate whose pyadi FIR this schema last loaded
|
|
92
93
|
|
|
93
94
|
@staticmethod
|
|
94
95
|
def make_device(**kwargs):
|
|
@@ -106,6 +107,7 @@ class PlutoSchema(DeviceSchema):
|
|
|
106
107
|
|
|
107
108
|
@idl_expose(kind="set")
|
|
108
109
|
def set_filter(self, value):
|
|
110
|
+
self._fir_rate = None
|
|
109
111
|
self.device.filter = value
|
|
110
112
|
|
|
111
113
|
@idl_expose(kind="get")
|
|
@@ -164,7 +166,20 @@ class PlutoSchema(DeviceSchema):
|
|
|
164
166
|
|
|
165
167
|
@idl_expose(kind="set")
|
|
166
168
|
def set_sample_rate(self, value):
|
|
169
|
+
# pyadi reloads its FIR and re-runs the AD9361 rate chain (~0.8 s) even
|
|
170
|
+
# for an unchanged rate: skip only what our own last set left in place.
|
|
171
|
+
if value == self._fir_rate:
|
|
172
|
+
try:
|
|
173
|
+
if (
|
|
174
|
+
self.device.sample_rate == value
|
|
175
|
+
and self.device._get_iio_attr("out", "voltage_filter_fir_en", False) == 1
|
|
176
|
+
):
|
|
177
|
+
return
|
|
178
|
+
except Exception:
|
|
179
|
+
pass
|
|
180
|
+
self._fir_rate = None
|
|
167
181
|
self.device.sample_rate = value
|
|
182
|
+
self._fir_rate = value
|
|
168
183
|
|
|
169
184
|
@idl_expose(kind="get")
|
|
170
185
|
def get_rx_lo(self):
|
|
@@ -222,12 +237,18 @@ class PlutoSchema(DeviceSchema):
|
|
|
222
237
|
def reset_for_next_holder(self):
|
|
223
238
|
"""A reservation ended: a PoE Pluto powers off. Otherwise stop its cyclic
|
|
224
239
|
transmission, and drop its RX buffer so the next holder's rx_buffer_size takes effect."""
|
|
240
|
+
self._fir_rate = None
|
|
225
241
|
if isinstance(self.device, PoePluto):
|
|
226
242
|
self.device.power_down()
|
|
227
243
|
elif self.device is not None:
|
|
228
244
|
self.device.tx_destroy_buffer()
|
|
229
245
|
self.device.rx_destroy_buffer()
|
|
230
246
|
|
|
247
|
+
def prepare_for_holder(self):
|
|
248
|
+
"""A reservation started: a PoE Pluto boots now, not at the holder's first command."""
|
|
249
|
+
if isinstance(self.device, PoePluto):
|
|
250
|
+
self.device.power_up()
|
|
251
|
+
|
|
231
252
|
@idl_expose(kind="call")
|
|
232
253
|
def call_disable_dds(self):
|
|
233
254
|
"""Disable the internal DDS tone generator."""
|
|
@@ -244,12 +265,14 @@ class PlutoSchema(DeviceSchema):
|
|
|
244
265
|
@idl_expose(kind="call")
|
|
245
266
|
def call_power_down(self):
|
|
246
267
|
"""PoE Plutos: cut power; the next command powers it back up. No-op on other Plutos."""
|
|
268
|
+
self._fir_rate = None
|
|
247
269
|
if isinstance(self.device, PoePluto):
|
|
248
270
|
self.device.power_down()
|
|
249
271
|
|
|
250
272
|
@idl_expose(kind="call")
|
|
251
273
|
def call_power_cycle(self):
|
|
252
274
|
"""PoE Plutos: power off, then on, and wait for boot. No-op on other Plutos."""
|
|
275
|
+
self._fir_rate = None
|
|
253
276
|
if isinstance(self.device, PoePluto):
|
|
254
277
|
self.device.power_cycle()
|
|
255
278
|
|
{remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/adalm_pluto/poe.py
RENAMED
|
@@ -32,6 +32,9 @@ IIOD_PORT = 30431
|
|
|
32
32
|
BOOT_TIMEOUT_S = 45
|
|
33
33
|
LINK_CHECK_S = 30 # no IIOD by then: ask the switch whether the port even has a link
|
|
34
34
|
OFF_S = 3 # long enough for the Pluto to lose power before it is switched back on
|
|
35
|
+
# Reservations starting together (the top of an hour) power every Pluto at
|
|
36
|
+
# once: keep the switch's API logins to a few at a time.
|
|
37
|
+
_SWITCH_SESSIONS = threading.BoundedSemaphore(4)
|
|
35
38
|
|
|
36
39
|
|
|
37
40
|
class RouterOS:
|
|
@@ -85,20 +88,22 @@ class RouterOS:
|
|
|
85
88
|
|
|
86
89
|
|
|
87
90
|
def set_poe(*, switch, user, password, port, on):
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
with _SWITCH_SESSIONS:
|
|
92
|
+
ros = RouterOS(switch, user, password)
|
|
93
|
+
try:
|
|
94
|
+
ros.cmd("/interface/ethernet/poe/set", f"=.id={port}", f"=poe-out={'auto-on' if on else 'off'}")
|
|
95
|
+
finally:
|
|
96
|
+
ros.close()
|
|
93
97
|
|
|
94
98
|
|
|
95
99
|
def port_running(*, switch, user, password, port):
|
|
96
100
|
"""Whether the switch port has an Ethernet link (RouterOS 'running')."""
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
101
|
+
with _SWITCH_SESSIONS:
|
|
102
|
+
ros = RouterOS(switch, user, password)
|
|
103
|
+
try:
|
|
104
|
+
replies = ros.cmd("/interface/ethernet/print", f"?name={port}", "=.proplist=name,running")
|
|
105
|
+
finally:
|
|
106
|
+
ros.close()
|
|
102
107
|
for sentence in replies:
|
|
103
108
|
if sentence[0] == "!re":
|
|
104
109
|
return "=running=true" in sentence
|
|
@@ -133,7 +138,14 @@ class PoePluto:
|
|
|
133
138
|
return f"<PoE Pluto {self.uri} on {self._port}, {'on' if self._pluto is not None else 'off'}>"
|
|
134
139
|
|
|
135
140
|
def _poe(self, on):
|
|
136
|
-
|
|
141
|
+
# The switch API occasionally times out; a lost power-off leaves the Pluto on.
|
|
142
|
+
for attempt in range(3):
|
|
143
|
+
try:
|
|
144
|
+
return set_poe(port=self._port, on=on, **self._switch)
|
|
145
|
+
except OSError:
|
|
146
|
+
if attempt == 2:
|
|
147
|
+
raise
|
|
148
|
+
time.sleep(2)
|
|
137
149
|
|
|
138
150
|
@property
|
|
139
151
|
def power_state(self):
|
|
@@ -25,6 +25,13 @@ from remoteRF_server.common.idl import DeviceSchema, idl_expose, idl_register
|
|
|
25
25
|
|
|
26
26
|
_MAX_REMOTE_PAYLOAD_BYTES = 4 * 1024 * 1024
|
|
27
27
|
_MAX_COMPLEX64_SAMPLES = _MAX_REMOTE_PAYLOAD_BYTES // np.dtype(np.complex64).itemsize
|
|
28
|
+
# librtlsdr's synchronous read fails on a partial USB packet, and pyrtlsdr then
|
|
29
|
+
# closes the device but keeps its freed handle: read whole packets and slice.
|
|
30
|
+
_USB_PACKET_BYTES = 512
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _whole_packets(num_bytes: int) -> int:
|
|
34
|
+
return -(-num_bytes // _USB_PACKET_BYTES) * _USB_PACKET_BYTES
|
|
28
35
|
|
|
29
36
|
def _require_rtlsdr():
|
|
30
37
|
try:
|
|
@@ -151,7 +158,8 @@ class RtlSdrSchema(DeviceSchema):
|
|
|
151
158
|
@property
|
|
152
159
|
def _native(self):
|
|
153
160
|
native = getattr(self.device, "native", None)
|
|
154
|
-
|
|
161
|
+
# pyrtlsdr closes itself on a failed read; its handle is freed then.
|
|
162
|
+
if native is None or getattr(native, "device_opened", True) is False:
|
|
155
163
|
raise RuntimeError("RTL-SDR device is closed")
|
|
156
164
|
return native
|
|
157
165
|
|
|
@@ -319,7 +327,8 @@ class RtlSdrSchema(DeviceSchema):
|
|
|
319
327
|
label="num_samples",
|
|
320
328
|
maximum=_MAX_COMPLEX64_SAMPLES,
|
|
321
329
|
)
|
|
322
|
-
|
|
330
|
+
read = _whole_packets(2 * count) // 2
|
|
331
|
+
samples = np.asarray(self._native.read_samples(read), dtype=np.complex64)[:count]
|
|
323
332
|
if samples.size != count:
|
|
324
333
|
raise RuntimeError(
|
|
325
334
|
f"short RTL-SDR read: requested {count}, received {samples.size}"
|
|
@@ -334,7 +343,7 @@ class RtlSdrSchema(DeviceSchema):
|
|
|
334
343
|
label="num_bytes",
|
|
335
344
|
maximum=_MAX_REMOTE_PAYLOAD_BYTES,
|
|
336
345
|
)
|
|
337
|
-
payload = bytes(self._native.read_bytes(count))
|
|
346
|
+
payload = bytes(self._native.read_bytes(_whole_packets(count)))[:count]
|
|
338
347
|
if len(payload) != count:
|
|
339
348
|
raise RuntimeError(
|
|
340
349
|
f"short RTL-SDR byte read: requested {count}, received {len(payload)}"
|
{remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/drivers/usrp/usrp_schema.py
RENAMED
|
@@ -27,6 +27,9 @@ c = idl_client
|
|
|
27
27
|
|
|
28
28
|
import itertools
|
|
29
29
|
import os
|
|
30
|
+
import subprocess
|
|
31
|
+
import sys
|
|
32
|
+
import threading
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
def _configure_uhd_console_logging():
|
|
@@ -114,8 +117,7 @@ def _remote_transport_args(value):
|
|
|
114
117
|
return f"{args},{setting}" if args else setting
|
|
115
118
|
|
|
116
119
|
|
|
117
|
-
def
|
|
118
|
-
uhd_module = _require_uhd()
|
|
120
|
+
def _usrp_args(args=None, serial=None, addr=None, **kwargs):
|
|
119
121
|
args = args or kwargs.pop("device_args", None) or kwargs.pop("init_args", None)
|
|
120
122
|
type_hint = kwargs.pop("type", None) or kwargs.pop("device_type", None)
|
|
121
123
|
profile_name = kwargs.pop("profile", None)
|
|
@@ -165,8 +167,53 @@ def _connect_usrp(args=None, serial=None, addr=None, **kwargs):
|
|
|
165
167
|
if profile.uhd_device_type and "type" not in arg_keys:
|
|
166
168
|
args = f"type={profile.uhd_device_type},{arg_text}"
|
|
167
169
|
|
|
168
|
-
|
|
170
|
+
return _remote_transport_args(args)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# A cold B2xx open (firmware + FPGA image) took ~68 s inside MultiUSRP(),
|
|
174
|
+
# which holds the GIL, so the whole lab stopped answering. A child process
|
|
175
|
+
# pays that instead while this one waits without the GIL; the open after it
|
|
176
|
+
# is warm (~2.5 s).
|
|
177
|
+
# ponytail: "warm" is per process; a USB replug while the Server runs makes
|
|
178
|
+
# the next open cold in-process again. Clear the mark on hotplug if that bites.
|
|
179
|
+
_PRELOAD_TIMEOUT = 180.0
|
|
180
|
+
_preloaded = set()
|
|
181
|
+
_preload_locks = {}
|
|
182
|
+
_preload_guard = threading.Lock()
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _preload_image(args):
|
|
186
|
+
if str(os.getenv("REMOTERF_USRP_PRELOAD", "1")).strip().lower() in {"0", "false", "no", "off"}:
|
|
187
|
+
return
|
|
188
|
+
with _preload_guard:
|
|
189
|
+
lock = _preload_locks.setdefault(args, threading.Lock())
|
|
190
|
+
# One device, one driver at a time: an open waits here for a pre-load.
|
|
191
|
+
with lock:
|
|
192
|
+
if args in _preloaded:
|
|
193
|
+
return
|
|
194
|
+
try:
|
|
195
|
+
done = subprocess.run(
|
|
196
|
+
[sys.executable, "-c", "import sys, uhd; uhd.usrp.MultiUSRP(sys.argv[1])", args],
|
|
197
|
+
stdin=subprocess.DEVNULL,
|
|
198
|
+
stdout=subprocess.DEVNULL,
|
|
199
|
+
stderr=subprocess.DEVNULL,
|
|
200
|
+
timeout=_PRELOAD_TIMEOUT,
|
|
201
|
+
)
|
|
202
|
+
except (OSError, subprocess.TimeoutExpired) as exc:
|
|
203
|
+
print(f"Warning: USRP image pre-load for {args!r} failed: {exc}")
|
|
204
|
+
return
|
|
205
|
+
if done.returncode == 0:
|
|
206
|
+
_preloaded.add(args)
|
|
207
|
+
else:
|
|
208
|
+
print(f"Warning: USRP image pre-load for {args!r} exited {done.returncode}")
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _connect_usrp(args=None, serial=None, addr=None, **kwargs):
|
|
212
|
+
uhd_module = _require_uhd()
|
|
213
|
+
args = _usrp_args(args, serial, addr, **kwargs)
|
|
214
|
+
_preload_image(args)
|
|
169
215
|
dev = uhd_module.usrp.MultiUSRP(args)
|
|
216
|
+
_preloaded.add(args)
|
|
170
217
|
return dev
|
|
171
218
|
|
|
172
219
|
|
|
@@ -618,6 +665,11 @@ class UsrpSchema(DeviceSchema):
|
|
|
618
665
|
def open_native_device(self):
|
|
619
666
|
return _connect_usrp(**self._inventory)
|
|
620
667
|
|
|
668
|
+
def prepare_for_holder(self):
|
|
669
|
+
"""A reservation started: load a cold image now, not in the holder's open."""
|
|
670
|
+
if uhd is not None:
|
|
671
|
+
_preload_image(_usrp_args(**self._inventory))
|
|
672
|
+
|
|
621
673
|
def create_session_schema(self):
|
|
622
674
|
"""Create an isolated, live adapter for one authenticated reservation."""
|
|
623
675
|
clone = type(self)()
|
{remoterf_server-1.1.3 → remoterf_server-1.4.2}/src/remoteRF_server/host/host_directory_store.py
RENAMED
|
@@ -125,6 +125,31 @@ class EnvStore:
|
|
|
125
125
|
|
|
126
126
|
self._write_lines(lines)
|
|
127
127
|
|
|
128
|
+
def remove_from_csv_list(self, key: str, value: str) -> None:
|
|
129
|
+
key = sanitize_env_key(key)
|
|
130
|
+
value = (value or "").strip()
|
|
131
|
+
|
|
132
|
+
with self._lock:
|
|
133
|
+
lines = self._read_lines()
|
|
134
|
+
for i, ln in enumerate(lines):
|
|
135
|
+
if ln.startswith(key + "="):
|
|
136
|
+
existing = csv_split(ln[len(key) + 1:])
|
|
137
|
+
if value not in existing:
|
|
138
|
+
return
|
|
139
|
+
existing.remove(value)
|
|
140
|
+
lines[i] = f"{key}=" + ",".join(existing)
|
|
141
|
+
self._write_lines(lines)
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
def remove_keys(self, keys: List[str]) -> None:
|
|
145
|
+
prefixes = tuple(sanitize_env_key(k) + "=" for k in keys)
|
|
146
|
+
|
|
147
|
+
with self._lock:
|
|
148
|
+
lines = self._read_lines()
|
|
149
|
+
kept = [ln for ln in lines if not ln.startswith(prefixes)]
|
|
150
|
+
if len(kept) != len(lines):
|
|
151
|
+
self._write_lines(kept)
|
|
152
|
+
|
|
128
153
|
def upsert_kv(self, key: str, value: str) -> None:
|
|
129
154
|
key = sanitize_env_key(key)
|
|
130
155
|
value = (value or "").strip()
|