remoteRF-server 0.1.136__tar.gz → 0.2.0__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-0.1.136 → remoterf_server-0.2.0}/PKG-INFO +2 -1
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/pyproject.toml +3 -2
- remoterf_server-0.2.0/src/remoteRF_server/common/idl/__init__.py +1 -0
- remoterf_server-0.2.0/src/remoteRF_server/common/idl/device_schema.py +39 -0
- remoterf_server-0.2.0/src/remoteRF_server/common/idl/pluto_schema.py +174 -0
- remoterf_server-0.2.0/src/remoteRF_server/common/idl/schema.py +356 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/__init__.py +1 -1
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/db_location.py +3 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/device_manager.py +59 -76
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/reservation.py +0 -13
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/tools/gist_status.py +1 -1
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server.egg-info/PKG-INFO +2 -1
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server.egg-info/SOURCES.txt +4 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server.egg-info/requires.txt +1 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/README.md +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/setup.cfg +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/__init__.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/__init__.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/grpc/__init__.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/grpc/grpc_host_pb2.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/grpc/grpc_pb2.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/ansi_codes.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/api_token.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/db_connection.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/list_string.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/process_arg.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/drivers/__init__.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/drivers/adalm_pluto/pluto_remote_server.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/host/__init__.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/host/host_auth_token.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/host/host_directory_store.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/host/host_tunnel_server.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/__init__.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/acc_perms.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/cert_provider.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/grpc_server.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/rpc_manager.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/user_group_cli.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/user_group_handler.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/serverrf_cli.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/tools/__init__.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/tools/gen_certs.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/tools/gist_status_testing.py +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server.egg-info/dependency_links.txt +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server.egg-info/entry_points.txt +0 -0
- {remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoteRF-server
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Description-Content-Type: text/markdown
|
|
5
5
|
Requires-Dist: grpcio
|
|
6
6
|
Requires-Dist: protobuf
|
|
@@ -9,6 +9,7 @@ Requires-Dist: prompt_toolkit
|
|
|
9
9
|
Requires-Dist: python-dotenv
|
|
10
10
|
Requires-Dist: grpcio-tools
|
|
11
11
|
Requires-Dist: requests
|
|
12
|
+
Requires-Dist: pyyaml
|
|
12
13
|
|
|
13
14
|
# Remote RF Server Guide (Linux)
|
|
14
15
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "remoteRF-server"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.2.0"
|
|
8
8
|
dependencies = [
|
|
9
9
|
"grpcio",
|
|
10
10
|
"protobuf",
|
|
@@ -12,7 +12,8 @@ dependencies = [
|
|
|
12
12
|
"prompt_toolkit",
|
|
13
13
|
"python-dotenv",
|
|
14
14
|
"grpcio-tools",
|
|
15
|
-
"requests"
|
|
15
|
+
"requests",
|
|
16
|
+
"pyyaml"
|
|
16
17
|
]
|
|
17
18
|
readme = "README.md"
|
|
18
19
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .schema import DeviceSchema, idl_register, idl_expose
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# remoteRF_server/common/idl/schema_template.py
|
|
2
|
+
|
|
3
|
+
# Include any necessary device imports, etc!
|
|
4
|
+
|
|
5
|
+
# todo: 'throws error' if stuff isn't implemented, etc.
|
|
6
|
+
# todo: have device_manifest live in .config, etc. (not in codebase)
|
|
7
|
+
|
|
8
|
+
from . import *
|
|
9
|
+
|
|
10
|
+
# todo: would import the decorators from remoteRF_server ... -> which allows any admin script to run with the platform!
|
|
11
|
+
# todo: you would run a 'baking the driver' (so it populates onto the .config or something with the proper version, etc. so the server knows!)
|
|
12
|
+
|
|
13
|
+
class DeviceSchema:
|
|
14
|
+
|
|
15
|
+
device_type = "template" # MUST match device_type in the device manifest!
|
|
16
|
+
driver_version = "0.0.1"
|
|
17
|
+
|
|
18
|
+
# Returns instance of the device (ie: a class, etc!)
|
|
19
|
+
# Used by the server to actually 'connect' to the hardware devices!
|
|
20
|
+
# Define the proper args in the device Manifest -> pipes to this factory method to populate device list!
|
|
21
|
+
# todo: kwargs are EXACTLY the 'init' section of the device manifest!
|
|
22
|
+
@staticmethod
|
|
23
|
+
def make_device(**kwargs):
|
|
24
|
+
# Implement whatever logic you need to connect to the device (or init parameters, like BW, etc.)
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
# Called by the server on init!
|
|
28
|
+
def __init__(self, device_instance):
|
|
29
|
+
self.device = device_instance
|
|
30
|
+
|
|
31
|
+
@idl_expose # Decorators for each 'exposed' method/property!
|
|
32
|
+
def schema_info(self) -> float:
|
|
33
|
+
return f"{self.device_type} v{self.driver_version}"
|
|
34
|
+
|
|
35
|
+
# Example exposed method:
|
|
36
|
+
@idl_expose # Decorators for each 'exposed' method/property!
|
|
37
|
+
def some_method(self, arg1: float, arg2: float) -> float:
|
|
38
|
+
return self.device.some_method(arg1, arg2) # EXAMPLE
|
|
39
|
+
# raise NotImplementedError
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# ~/.config/remoterf/drivers/pluto_schema.py
|
|
2
|
+
#
|
|
3
|
+
# Admin-written schema for ADALM-Pluto devices.
|
|
4
|
+
# Drop this file into the drivers folder, restart the server. Done.
|
|
5
|
+
|
|
6
|
+
from . import *
|
|
7
|
+
|
|
8
|
+
import subprocess
|
|
9
|
+
import re
|
|
10
|
+
import adi
|
|
11
|
+
|
|
12
|
+
# ========================= PLUTO Connection Logic =========================
|
|
13
|
+
|
|
14
|
+
def connect_pluto(*, serial: str):
|
|
15
|
+
serial = (serial or "").strip()
|
|
16
|
+
if not serial:
|
|
17
|
+
print("A Pluto serial must be provided")
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
out = subprocess.check_output(
|
|
22
|
+
["iio_info", "-s"],
|
|
23
|
+
text=True,
|
|
24
|
+
stderr=subprocess.STDOUT
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
usb = None
|
|
28
|
+
for line in out.splitlines():
|
|
29
|
+
if (f"serial={serial}" in line) or (f"hw_serial={serial}" in line):
|
|
30
|
+
m = re.search(r"\[usb:([^\]]+)\]", line)
|
|
31
|
+
if m:
|
|
32
|
+
usb = m.group(1).strip()
|
|
33
|
+
break
|
|
34
|
+
|
|
35
|
+
if not usb:
|
|
36
|
+
print(f"No device found with serial {serial}")
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
dev = adi.Pluto(f"usb:{usb}")
|
|
40
|
+
print(f"Connected to Pluto serial={serial} via usb:{usb}")
|
|
41
|
+
return dev
|
|
42
|
+
|
|
43
|
+
except Exception as e:
|
|
44
|
+
print(f"Failed to connect to Pluto serial={serial}: {e}")
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
# ========================= PLUTO Schema =========================
|
|
48
|
+
|
|
49
|
+
@idl_register("pluto")
|
|
50
|
+
class PlutoSchema(DeviceSchema):
|
|
51
|
+
|
|
52
|
+
device_type = "pluto"
|
|
53
|
+
driver_version = "0.0.1"
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def make_device(**kwargs):
|
|
57
|
+
serial = kwargs.get("serial")
|
|
58
|
+
return connect_pluto(serial=serial)
|
|
59
|
+
|
|
60
|
+
# region ad9364
|
|
61
|
+
|
|
62
|
+
@idl_expose(kind="get")
|
|
63
|
+
def get_filter(self):
|
|
64
|
+
return self.device.filter
|
|
65
|
+
|
|
66
|
+
@idl_expose(kind="set")
|
|
67
|
+
def set_filter(self, value):
|
|
68
|
+
self.device.filter = value
|
|
69
|
+
|
|
70
|
+
@idl_expose(kind="get")
|
|
71
|
+
def get_loopback(self):
|
|
72
|
+
return self.device.loopback
|
|
73
|
+
|
|
74
|
+
@idl_expose(kind="set")
|
|
75
|
+
def set_loopback(self, value):
|
|
76
|
+
self.device.loopback = value
|
|
77
|
+
|
|
78
|
+
@idl_expose(kind="get")
|
|
79
|
+
def get_gain_control_mode_chan0(self):
|
|
80
|
+
return self.device.gain_control_mode_chan0
|
|
81
|
+
|
|
82
|
+
@idl_expose(kind="set")
|
|
83
|
+
def set_gain_control_mode_chan0(self, value):
|
|
84
|
+
self.device.gain_control_mode_chan0 = value
|
|
85
|
+
|
|
86
|
+
@idl_expose(kind="get")
|
|
87
|
+
def get_rx_hardwaregain_chan0(self):
|
|
88
|
+
return self.device.rx_hardwaregain_chan0
|
|
89
|
+
|
|
90
|
+
@idl_expose(kind="set")
|
|
91
|
+
def set_rx_hardwaregain_chan0(self, value):
|
|
92
|
+
self.device.rx_hardwaregain_chan0 = value
|
|
93
|
+
|
|
94
|
+
@idl_expose(kind="get")
|
|
95
|
+
def get_tx_hardwaregain_chan0(self):
|
|
96
|
+
return self.device.tx_hardwaregain_chan0
|
|
97
|
+
|
|
98
|
+
@idl_expose(kind="set")
|
|
99
|
+
def set_tx_hardwaregain_chan0(self, value):
|
|
100
|
+
self.device.tx_hardwaregain_chan0 = value
|
|
101
|
+
|
|
102
|
+
@idl_expose(kind="get")
|
|
103
|
+
def get_rx_rf_bandwidth(self):
|
|
104
|
+
return self.device.rx_rf_bandwidth
|
|
105
|
+
|
|
106
|
+
@idl_expose(kind="set")
|
|
107
|
+
def set_rx_rf_bandwidth(self, value):
|
|
108
|
+
self.device.rx_rf_bandwidth = value
|
|
109
|
+
|
|
110
|
+
@idl_expose(kind="get")
|
|
111
|
+
def get_tx_rf_bandwidth(self):
|
|
112
|
+
return self.device.tx_rf_bandwidth
|
|
113
|
+
|
|
114
|
+
@idl_expose(kind="set")
|
|
115
|
+
def set_tx_rf_bandwidth(self, value):
|
|
116
|
+
self.device.tx_rf_bandwidth = value
|
|
117
|
+
|
|
118
|
+
@idl_expose(kind="get")
|
|
119
|
+
def get_sample_rate(self):
|
|
120
|
+
return self.device.sample_rate
|
|
121
|
+
|
|
122
|
+
@idl_expose(kind="set")
|
|
123
|
+
def set_sample_rate(self, value):
|
|
124
|
+
self.device.sample_rate = value
|
|
125
|
+
|
|
126
|
+
@idl_expose(kind="get")
|
|
127
|
+
def get_rx_lo(self):
|
|
128
|
+
return self.device.rx_lo
|
|
129
|
+
|
|
130
|
+
@idl_expose(kind="set")
|
|
131
|
+
def set_rx_lo(self, value):
|
|
132
|
+
self.device.rx_lo = value
|
|
133
|
+
|
|
134
|
+
@idl_expose(kind="get")
|
|
135
|
+
def get_tx_lo(self):
|
|
136
|
+
return self.device.tx_lo
|
|
137
|
+
|
|
138
|
+
@idl_expose(kind="set")
|
|
139
|
+
def set_tx_lo(self, value):
|
|
140
|
+
self.device.tx_lo = value
|
|
141
|
+
|
|
142
|
+
@idl_expose(kind="get")
|
|
143
|
+
def get_tx_cyclic_buffer(self):
|
|
144
|
+
return self.device.tx_cyclic_buffer
|
|
145
|
+
|
|
146
|
+
@idl_expose(kind="set")
|
|
147
|
+
def set_tx_cyclic_buffer(self, value):
|
|
148
|
+
self.device.tx_cyclic_buffer = value
|
|
149
|
+
|
|
150
|
+
# region RX/TX
|
|
151
|
+
|
|
152
|
+
@idl_expose(kind="call")
|
|
153
|
+
def call_rx(self):
|
|
154
|
+
return self.device.rx()
|
|
155
|
+
|
|
156
|
+
@idl_expose(kind="get")
|
|
157
|
+
def get_rx_buffer_size(self):
|
|
158
|
+
return self.device.rx_buffer_size
|
|
159
|
+
|
|
160
|
+
@idl_expose(kind="set")
|
|
161
|
+
def set_rx_buffer_size(self, value):
|
|
162
|
+
self.device.rx_buffer_size = value
|
|
163
|
+
|
|
164
|
+
@idl_expose(kind="call")
|
|
165
|
+
def call_rx_destroy_buffer(self):
|
|
166
|
+
self.device.rx_destroy_buffer()
|
|
167
|
+
|
|
168
|
+
@idl_expose(kind="call")
|
|
169
|
+
def call_tx(self, value):
|
|
170
|
+
self.device.tx(value)
|
|
171
|
+
|
|
172
|
+
@idl_expose(kind="call")
|
|
173
|
+
def call_tx_destroy_buffer(self):
|
|
174
|
+
self.device.tx_destroy_buffer()
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# src/remoteRF_server/common/idl/schema.py
|
|
2
|
+
"""
|
|
3
|
+
IDL Schema framework.
|
|
4
|
+
|
|
5
|
+
Admin writes one .py per device TYPE, drops it in ~/.config/remoterf/drivers/,
|
|
6
|
+
and the server auto-discovers it on startup via load_drivers().
|
|
7
|
+
|
|
8
|
+
~/.config/remoterf/
|
|
9
|
+
devices.env ← "I have 3 plutos at these serials"
|
|
10
|
+
drivers/
|
|
11
|
+
pluto_schema.py ← "here's what a pluto IS"
|
|
12
|
+
hackrf_schema.py
|
|
13
|
+
webcam_schema.py
|
|
14
|
+
|
|
15
|
+
The admin's schema file:
|
|
16
|
+
1. Imports from this module
|
|
17
|
+
2. Subclasses DeviceSchema
|
|
18
|
+
3. Decorates with @register_driver("pluto")
|
|
19
|
+
4. Implements make_device(**kwargs) — factory from manifest config
|
|
20
|
+
5. Decorates exposed methods with @idl_expose(kind="get"|"set"|"call")
|
|
21
|
+
|
|
22
|
+
That's it. The server never needs to be modified.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import hashlib
|
|
27
|
+
import importlib.util
|
|
28
|
+
import json
|
|
29
|
+
import sys
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
from typing import Any, Dict, Optional
|
|
32
|
+
|
|
33
|
+
from ..utils import *
|
|
34
|
+
|
|
35
|
+
# ───────────────────────────────────────────────────────────────────
|
|
36
|
+
# @idl_expose decorator
|
|
37
|
+
# ───────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
_EXPOSE_KEY = "__idl__"
|
|
40
|
+
|
|
41
|
+
def idl_expose(_fn=None, *, kind: str = "call", doc: str = ""):
|
|
42
|
+
"""
|
|
43
|
+
Mark a method as exposed over IDL.
|
|
44
|
+
|
|
45
|
+
@idl_expose(kind="get")
|
|
46
|
+
def get_tx_lo(self):
|
|
47
|
+
return self.device.tx_lo
|
|
48
|
+
|
|
49
|
+
@idl_expose(kind="set")
|
|
50
|
+
def set_tx_lo(self, value):
|
|
51
|
+
self.device.tx_lo = value
|
|
52
|
+
|
|
53
|
+
@idl_expose(kind="call")
|
|
54
|
+
def call_rx(self):
|
|
55
|
+
return self.device.rx()
|
|
56
|
+
|
|
57
|
+
@idl_expose # bare — defaults to kind="call"
|
|
58
|
+
def some_method(self, x):
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
kind:
|
|
62
|
+
"get" — property getter (no args beyond self)
|
|
63
|
+
"set" — property setter (one arg: value)
|
|
64
|
+
"call" — general method (any args)
|
|
65
|
+
"""
|
|
66
|
+
def _apply(fn):
|
|
67
|
+
meta = {
|
|
68
|
+
"kind": kind,
|
|
69
|
+
"doc": doc or (fn.__doc__ or "").strip(),
|
|
70
|
+
}
|
|
71
|
+
setattr(fn, _EXPOSE_KEY, meta)
|
|
72
|
+
return fn
|
|
73
|
+
|
|
74
|
+
if _fn is not None:
|
|
75
|
+
return _apply(_fn)
|
|
76
|
+
return _apply
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
# ───────────────────────────────────────────────────────────────────
|
|
80
|
+
# Driver registry
|
|
81
|
+
# ───────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
_DRIVER_REGISTRY: Dict[str, type] = {}
|
|
84
|
+
|
|
85
|
+
def register_driver(device_type: str):
|
|
86
|
+
"""
|
|
87
|
+
Class decorator — registers a schema so the server can find it by type string.
|
|
88
|
+
|
|
89
|
+
@register_driver("pluto")
|
|
90
|
+
class PlutoSchema(DeviceSchema):
|
|
91
|
+
...
|
|
92
|
+
"""
|
|
93
|
+
def decorator(cls):
|
|
94
|
+
key = device_type.strip().lower()
|
|
95
|
+
_DRIVER_REGISTRY[key] = cls
|
|
96
|
+
return cls
|
|
97
|
+
return decorator
|
|
98
|
+
|
|
99
|
+
def get_driver_class(device_type: str) -> type:
|
|
100
|
+
cls = _DRIVER_REGISTRY.get(device_type.strip().lower())
|
|
101
|
+
if cls is None:
|
|
102
|
+
raise KeyError(
|
|
103
|
+
f"no driver for type '{device_type}'. "
|
|
104
|
+
f"registered: {list(_DRIVER_REGISTRY.keys()) or ['(none)']}"
|
|
105
|
+
)
|
|
106
|
+
return cls
|
|
107
|
+
|
|
108
|
+
def list_registered_drivers() -> Dict[str, str]:
|
|
109
|
+
return {
|
|
110
|
+
k: f"{cls.device_type} v{cls.driver_version}"
|
|
111
|
+
for k, cls in _DRIVER_REGISTRY.items()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# ───────────────────────────────────────────────────────────────────
|
|
115
|
+
# Plugin loader — auto-discover driver schemas from a folder
|
|
116
|
+
# ───────────────────────────────────────────────────────────────────
|
|
117
|
+
|
|
118
|
+
def load_drivers() -> Dict[str, str]:
|
|
119
|
+
"""
|
|
120
|
+
Import every .py in the drivers config dir. Each file's @idl_register
|
|
121
|
+
decorator self-registers into the global registry.
|
|
122
|
+
|
|
123
|
+
Returns {filename: status} for logging.
|
|
124
|
+
|
|
125
|
+
Called once at server startup:
|
|
126
|
+
load_drivers()
|
|
127
|
+
"""
|
|
128
|
+
drivers_dir = get_drivers_dir()
|
|
129
|
+
results: Dict[str, str] = {}
|
|
130
|
+
|
|
131
|
+
if not drivers_dir.is_dir():
|
|
132
|
+
return {"__error__": f"{drivers_dir} is not a directory"}
|
|
133
|
+
|
|
134
|
+
for f in sorted(drivers_dir.glob("*.py")):
|
|
135
|
+
if f.name.startswith("_"):
|
|
136
|
+
continue
|
|
137
|
+
try:
|
|
138
|
+
spec = importlib.util.spec_from_file_location(f.stem, str(f))
|
|
139
|
+
if spec is None or spec.loader is None:
|
|
140
|
+
results[f.name] = "skip: not a valid module"
|
|
141
|
+
continue
|
|
142
|
+
mod = importlib.util.module_from_spec(spec)
|
|
143
|
+
# Make our module importable from inside the driver file
|
|
144
|
+
# so `from schema import ...` works even when loaded as a plugin
|
|
145
|
+
sys.modules[f.stem] = mod
|
|
146
|
+
spec.loader.exec_module(mod)
|
|
147
|
+
results[f.name] = "ok"
|
|
148
|
+
except Exception as e:
|
|
149
|
+
results[f.name] = f"error: {e}"
|
|
150
|
+
|
|
151
|
+
return results
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# ───────────────────────────────────────────────────────────────────
|
|
155
|
+
# DeviceSchema base class
|
|
156
|
+
# ───────────────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
class DeviceSchema:
|
|
159
|
+
"""
|
|
160
|
+
Base for all device schemas. Admin subclasses this once per device type.
|
|
161
|
+
|
|
162
|
+
Class attributes (admin sets these):
|
|
163
|
+
device_type str — must match the TYPE in devices.env
|
|
164
|
+
driver_version str — semver
|
|
165
|
+
|
|
166
|
+
Admin implements:
|
|
167
|
+
make_device(**kwargs) — factory: manifest config → live device handle
|
|
168
|
+
returns the device object, or None on failure
|
|
169
|
+
|
|
170
|
+
Admin decorates exposed methods with @idl_expose(kind=...).
|
|
171
|
+
|
|
172
|
+
The server calls:
|
|
173
|
+
schema = PlutoSchema()
|
|
174
|
+
dev = schema.make_device(serial="00000001")
|
|
175
|
+
schema.device = dev # or schema.bind(dev)
|
|
176
|
+
schema.dispatch("get_tx_lo", {}) # → calls get_tx_lo()
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
device_type: str = ""
|
|
180
|
+
driver_version: str = ""
|
|
181
|
+
|
|
182
|
+
def __init__(self):
|
|
183
|
+
self.device = None # set after make_device()
|
|
184
|
+
self._exposed: Dict[str, _Exposed] = {}
|
|
185
|
+
self._schema_cache: dict | None = None
|
|
186
|
+
self._hash_cache: str | None = None
|
|
187
|
+
self._introspect()
|
|
188
|
+
|
|
189
|
+
# ── Factory (admin overrides) ─────────────────────────────────
|
|
190
|
+
|
|
191
|
+
@staticmethod
|
|
192
|
+
def make_device(**kwargs):
|
|
193
|
+
"""
|
|
194
|
+
Create and return a live device handle using kwargs from the manifest.
|
|
195
|
+
Admin MUST override this.
|
|
196
|
+
"""
|
|
197
|
+
raise NotImplementedError("make_device() must be implemented in the schema")
|
|
198
|
+
|
|
199
|
+
# ── Bind device to schema instance ────────────────────────────
|
|
200
|
+
|
|
201
|
+
def bind(self, device) -> None:
|
|
202
|
+
"""Attach a live device handle so exposed methods can use self.device."""
|
|
203
|
+
self.device = device
|
|
204
|
+
|
|
205
|
+
def is_connected(self) -> bool:
|
|
206
|
+
return self.device is not None
|
|
207
|
+
|
|
208
|
+
# ── Introspection ─────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
def _introspect(self):
|
|
211
|
+
seen: set = set()
|
|
212
|
+
for klass in type(self).__mro__:
|
|
213
|
+
for name, obj in vars(klass).items():
|
|
214
|
+
if name in seen or name.startswith("_"):
|
|
215
|
+
continue
|
|
216
|
+
meta = getattr(obj, _EXPOSE_KEY, None)
|
|
217
|
+
if meta is None:
|
|
218
|
+
continue
|
|
219
|
+
seen.add(name)
|
|
220
|
+
self._exposed[name] = _Exposed(
|
|
221
|
+
name=name, fn=obj, kind=meta["kind"], doc=meta["doc"],
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
# ── IDL generation ────────────────────────────────────────────
|
|
225
|
+
|
|
226
|
+
def get_idl(self) -> dict:
|
|
227
|
+
"""Full wire-ready schema dict with content-addressed hash."""
|
|
228
|
+
if self._schema_cache is not None:
|
|
229
|
+
return self._schema_cache
|
|
230
|
+
|
|
231
|
+
body = self._build_body()
|
|
232
|
+
canon = json.dumps(body, sort_keys=True, separators=(",", ":"))
|
|
233
|
+
h = f"sha256:{hashlib.sha256(canon.encode()).hexdigest()}"
|
|
234
|
+
|
|
235
|
+
self._hash_cache = h
|
|
236
|
+
self._schema_cache = {**body, "schema_hash": h}
|
|
237
|
+
return self._schema_cache
|
|
238
|
+
|
|
239
|
+
def get_idl_hash(self) -> str:
|
|
240
|
+
if self._hash_cache is None:
|
|
241
|
+
self.get_idl()
|
|
242
|
+
return self._hash_cache # type: ignore
|
|
243
|
+
|
|
244
|
+
def get_idl_json(self, *, pretty: bool = False) -> str:
|
|
245
|
+
d = self.get_idl()
|
|
246
|
+
if pretty:
|
|
247
|
+
return json.dumps(d, indent=2, sort_keys=True)
|
|
248
|
+
return json.dumps(d, sort_keys=True, separators=(",", ":"))
|
|
249
|
+
|
|
250
|
+
def _build_body(self) -> dict:
|
|
251
|
+
getters = {}
|
|
252
|
+
setters = {}
|
|
253
|
+
calls = {}
|
|
254
|
+
|
|
255
|
+
for name, ex in sorted(self._exposed.items()):
|
|
256
|
+
entry = {"doc": ex.doc} if ex.doc else {}
|
|
257
|
+
if ex.kind == "get":
|
|
258
|
+
getters[name] = entry
|
|
259
|
+
elif ex.kind == "set":
|
|
260
|
+
setters[name] = entry
|
|
261
|
+
elif ex.kind == "call":
|
|
262
|
+
calls[name] = entry
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
"schema_version": "1.0",
|
|
266
|
+
"device_type": self.device_type,
|
|
267
|
+
"driver_version": self.driver_version,
|
|
268
|
+
"getters": getters,
|
|
269
|
+
"setters": setters,
|
|
270
|
+
"calls": calls,
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# ── Dispatch (server RPC layer calls this) ────────────────────
|
|
274
|
+
|
|
275
|
+
def dispatch(self, method_name: str, args: dict) -> Any:
|
|
276
|
+
"""
|
|
277
|
+
Called by the RPC layer. Finds the exposed method and calls it.
|
|
278
|
+
|
|
279
|
+
dispatch("get_tx_lo", {}) → self.get_tx_lo()
|
|
280
|
+
dispatch("set_tx_lo", {"value": 2.4e9}) → self.set_tx_lo(2.4e9)
|
|
281
|
+
dispatch("call_rx", {}) → self.call_rx()
|
|
282
|
+
dispatch("call_tx", {"value": samples}) → self.call_tx(samples)
|
|
283
|
+
"""
|
|
284
|
+
ex = self._exposed.get(method_name)
|
|
285
|
+
if ex is None:
|
|
286
|
+
raise KeyError(
|
|
287
|
+
f"'{method_name}' is not exposed on {self.device_type}. "
|
|
288
|
+
f"Available: {list(self._exposed.keys())}"
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
if self.device is None:
|
|
292
|
+
raise RuntimeError(f"no device bound to {self.device_type} schema")
|
|
293
|
+
|
|
294
|
+
if ex.kind == "get":
|
|
295
|
+
return ex.fn(self)
|
|
296
|
+
|
|
297
|
+
elif ex.kind == "set":
|
|
298
|
+
value = args.get("value")
|
|
299
|
+
if value is None and "value" not in args:
|
|
300
|
+
# Also check first positional-style arg key
|
|
301
|
+
for k, v in args.items():
|
|
302
|
+
value = v
|
|
303
|
+
break
|
|
304
|
+
return ex.fn(self, value)
|
|
305
|
+
|
|
306
|
+
elif ex.kind == "call":
|
|
307
|
+
# Calls can have 0 or more args
|
|
308
|
+
# Simple convention: if the function takes 'value', pass it
|
|
309
|
+
import inspect
|
|
310
|
+
sig = inspect.signature(ex.fn)
|
|
311
|
+
params = [p for p in sig.parameters if p != "self"]
|
|
312
|
+
|
|
313
|
+
if len(params) == 0:
|
|
314
|
+
return ex.fn(self)
|
|
315
|
+
elif len(params) == 1:
|
|
316
|
+
# Single arg — pull from args dict by param name or "value"
|
|
317
|
+
pname = params[0]
|
|
318
|
+
val = args.get(pname, args.get("value"))
|
|
319
|
+
return ex.fn(self, val)
|
|
320
|
+
else:
|
|
321
|
+
# Multi-arg — match by name
|
|
322
|
+
call_args = {p: args.get(p) for p in params if p in args}
|
|
323
|
+
return ex.fn(self, **call_args)
|
|
324
|
+
else:
|
|
325
|
+
raise ValueError(f"unknown kind '{ex.kind}' on {method_name}")
|
|
326
|
+
|
|
327
|
+
# ── Debug ─────────────────────────────────────────────────────
|
|
328
|
+
|
|
329
|
+
def list_exposed(self) -> dict:
|
|
330
|
+
by_kind: Dict[str, list] = {"get": [], "set": [], "call": []}
|
|
331
|
+
for name, ex in sorted(self._exposed.items()):
|
|
332
|
+
by_kind.get(ex.kind, []).append(name)
|
|
333
|
+
return {
|
|
334
|
+
"device_type": self.device_type,
|
|
335
|
+
"driver_version": self.driver_version,
|
|
336
|
+
"connected": self.is_connected(),
|
|
337
|
+
"getters": by_kind["get"],
|
|
338
|
+
"setters": by_kind["set"],
|
|
339
|
+
"calls": by_kind["call"],
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
def __repr__(self) -> str:
|
|
343
|
+
return f"<{type(self).__name__} type={self.device_type!r} connected={self.is_connected()}>"
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
# ───────────────────────────────────────────────────────────────────
|
|
347
|
+
# Internal
|
|
348
|
+
# ───────────────────────────────────────────────────────────────────
|
|
349
|
+
|
|
350
|
+
class _Exposed:
|
|
351
|
+
__slots__ = ("name", "fn", "kind", "doc")
|
|
352
|
+
def __init__(self, *, name, fn, kind, doc):
|
|
353
|
+
self.name = name
|
|
354
|
+
self.fn = fn
|
|
355
|
+
self.kind = kind
|
|
356
|
+
self.doc = doc
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/__init__.py
RENAMED
|
@@ -3,4 +3,4 @@ from .process_arg import unmap_arg, map_arg
|
|
|
3
3
|
from .ansi_codes import printf, stylize, Sty
|
|
4
4
|
from .list_string import list_to_str, str_to_list
|
|
5
5
|
from .db_connection import db_connection
|
|
6
|
-
from .db_location import get_db_dir
|
|
6
|
+
from .db_location import get_db_dir, get_certs_dir, get_drivers_dir, get_remoterf_root
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/device_manager.py
RENAMED
|
@@ -6,6 +6,7 @@ import os
|
|
|
6
6
|
import re
|
|
7
7
|
import time
|
|
8
8
|
import adi
|
|
9
|
+
import yaml
|
|
9
10
|
import subprocess
|
|
10
11
|
import threading
|
|
11
12
|
from dataclasses import dataclass, field
|
|
@@ -13,7 +14,7 @@ from pathlib import Path
|
|
|
13
14
|
from typing import Dict, Tuple, Optional, Any, List, Iterator
|
|
14
15
|
from contextlib import contextmanager
|
|
15
16
|
|
|
16
|
-
from ..common.utils import validate_token
|
|
17
|
+
from ..common.utils import validate_token, get_remoterf_root
|
|
17
18
|
from ..host import host_tunnel_server as hts
|
|
18
19
|
|
|
19
20
|
_state_lock = threading.RLock()
|
|
@@ -226,17 +227,67 @@ def _host_devices_str_snapshot() -> Dict[int, str]:
|
|
|
226
227
|
|
|
227
228
|
# Config paths (server-side)
|
|
228
229
|
|
|
229
|
-
def
|
|
230
|
-
return Path(os.getenv("
|
|
230
|
+
def _cfg_dir() -> Path:
|
|
231
|
+
return Path(os.getenv("REMOTERF_CONFIG_DIR", get_remoterf_root()))
|
|
231
232
|
|
|
233
|
+
def _devices_yaml_path() -> Path:
|
|
234
|
+
p1 = _cfg_dir() / "devices.yml"
|
|
235
|
+
if p1.exists():
|
|
236
|
+
return p1
|
|
237
|
+
return _cfg_dir() / "devices.yaml"
|
|
232
238
|
|
|
233
|
-
def
|
|
234
|
-
|
|
235
|
-
|
|
239
|
+
def _load_device_records() -> Dict[int, Dict[str, str]]:
|
|
240
|
+
path = _devices_yaml_path()
|
|
241
|
+
if not path.exists():
|
|
242
|
+
return {}
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
with path.open("r", encoding="utf-8") as f:
|
|
246
|
+
data = yaml.safe_load(f) or {}
|
|
247
|
+
except Exception as e:
|
|
248
|
+
print(f"Error reading YAML config {path}: {e}")
|
|
249
|
+
return {}
|
|
250
|
+
|
|
251
|
+
devices = data.get("devices") or []
|
|
252
|
+
if not isinstance(devices, list):
|
|
253
|
+
print(f"Invalid YAML config {path}: 'devices' must be a list")
|
|
254
|
+
return {}
|
|
255
|
+
|
|
256
|
+
recs: Dict[int, Dict[str, str]] = {}
|
|
257
|
+
|
|
258
|
+
for item in devices:
|
|
259
|
+
if not isinstance(item, dict):
|
|
260
|
+
continue
|
|
261
|
+
|
|
262
|
+
try:
|
|
263
|
+
gid = int(item["device_id"])
|
|
264
|
+
except Exception:
|
|
265
|
+
print(f"Skipping device entry with invalid/missing device_id: {item}")
|
|
266
|
+
continue
|
|
267
|
+
|
|
268
|
+
dtype = str(item.get("device_type") or "pluto").strip().lower()
|
|
269
|
+
if dtype != "pluto":
|
|
270
|
+
print(f"Skipping unsupported device_type={dtype!r} for gid={gid}")
|
|
271
|
+
continue
|
|
236
272
|
|
|
273
|
+
name = str(item.get("name") or f"device-{gid}").strip()
|
|
274
|
+
init = item.get("init") or {}
|
|
275
|
+
if not isinstance(init, dict):
|
|
276
|
+
init = {}
|
|
237
277
|
|
|
238
|
-
|
|
239
|
-
|
|
278
|
+
serial = str(init.get("serial") or "").strip()
|
|
279
|
+
if not serial:
|
|
280
|
+
print(f"Skipping Pluto gid={gid}: missing init.serial")
|
|
281
|
+
continue
|
|
282
|
+
|
|
283
|
+
recs[gid] = {
|
|
284
|
+
"TYPE": "pluto",
|
|
285
|
+
"NAME": name,
|
|
286
|
+
"IDENT_KIND": "iio_serial",
|
|
287
|
+
"IDENT": serial,
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return recs
|
|
240
291
|
|
|
241
292
|
# Pluto helpers
|
|
242
293
|
|
|
@@ -273,74 +324,6 @@ def get_usb_port_from_serial(serial: str) -> str | None:
|
|
|
273
324
|
print(f"No device found with serial {serial}")
|
|
274
325
|
return None
|
|
275
326
|
|
|
276
|
-
# Parse devices.env
|
|
277
|
-
|
|
278
|
-
_ENV_LINE = re.compile(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$")
|
|
279
|
-
_DEVICE_KEY_RE = re.compile(r"^DEVICE_(\d+)_(.+)$", re.IGNORECASE)
|
|
280
|
-
|
|
281
|
-
def _strip_quotes(v: str) -> str:
|
|
282
|
-
v = (v or "").strip()
|
|
283
|
-
if len(v) >= 2 and ((v[0] == v[-1] == '"') or (v[0] == v[-1] == "'")):
|
|
284
|
-
return v[1:-1]
|
|
285
|
-
return v
|
|
286
|
-
|
|
287
|
-
def _read_env_file(path: Path) -> Dict[str, str]:
|
|
288
|
-
out: Dict[str, str] = {}
|
|
289
|
-
if not path.exists():
|
|
290
|
-
return out
|
|
291
|
-
try:
|
|
292
|
-
txt = path.read_text(encoding="utf-8")
|
|
293
|
-
except Exception:
|
|
294
|
-
return out
|
|
295
|
-
|
|
296
|
-
for raw in txt.splitlines():
|
|
297
|
-
line = raw.strip()
|
|
298
|
-
if not line or line.startswith("#"):
|
|
299
|
-
continue
|
|
300
|
-
m = _ENV_LINE.match(line)
|
|
301
|
-
if not m:
|
|
302
|
-
continue
|
|
303
|
-
k = m.group(1).strip()
|
|
304
|
-
v = _strip_quotes(m.group(2))
|
|
305
|
-
out[k] = v
|
|
306
|
-
return out
|
|
307
|
-
|
|
308
|
-
def _load_device_records() -> Dict[int, Dict[str, str]]:
|
|
309
|
-
# Returns: { gid: { "TYPE":..., "NAME":..., "IDENT_KIND":..., "IDENT":..., ... } }
|
|
310
|
-
|
|
311
|
-
kv = _read_env_file(_devices_env_path())
|
|
312
|
-
recs: Dict[int, Dict[str, str]] = {}
|
|
313
|
-
|
|
314
|
-
# New format: DEVICE_<gid>_<FIELD>
|
|
315
|
-
for k, v in kv.items():
|
|
316
|
-
m = _DEVICE_KEY_RE.match(k.strip())
|
|
317
|
-
if not m:
|
|
318
|
-
continue
|
|
319
|
-
try:
|
|
320
|
-
gid = int(m.group(1))
|
|
321
|
-
except Exception:
|
|
322
|
-
continue
|
|
323
|
-
field = m.group(2).strip().upper()
|
|
324
|
-
recs.setdefault(gid, {})[field] = str(v).strip()
|
|
325
|
-
|
|
326
|
-
# Back-compat: DEVICE_<gid>_ID treated as IDENT
|
|
327
|
-
for k, v in kv.items():
|
|
328
|
-
kk = k.strip().upper()
|
|
329
|
-
if not kk.startswith("DEVICE_") or not kk.endswith("_ID"):
|
|
330
|
-
continue
|
|
331
|
-
mid = kk[7:-3]
|
|
332
|
-
try:
|
|
333
|
-
gid = int(mid)
|
|
334
|
-
except Exception:
|
|
335
|
-
continue
|
|
336
|
-
recs.setdefault(gid, {})
|
|
337
|
-
recs[gid].setdefault("TYPE", "pluto")
|
|
338
|
-
recs[gid].setdefault("NAME", f"device-{gid}")
|
|
339
|
-
recs[gid].setdefault("IDENT_KIND", "iio_serial")
|
|
340
|
-
recs[gid].setdefault("IDENT", str(v).strip())
|
|
341
|
-
|
|
342
|
-
return recs
|
|
343
|
-
|
|
344
327
|
def _connect_from_record(rec: Dict[str, str]):
|
|
345
328
|
# Returns (device_obj, ident, dtype).
|
|
346
329
|
|
|
@@ -99,11 +99,6 @@ class ReservationHandler:
|
|
|
99
99
|
password = unmap_arg(args['pw'])
|
|
100
100
|
if self.login_user(username, password) == {"UC": map_arg(f'{username}')}:
|
|
101
101
|
if self.validate_cancel_reservation(unmap_arg(args['res_id']), username):
|
|
102
|
-
res_info = self.get_reservation_details(unmap_arg(args['res_id']))
|
|
103
|
-
if res_info:
|
|
104
|
-
args['cancelled_device_id'] = map_arg(res_info['device_id'])
|
|
105
|
-
args['cancelled_start_time'] = map_arg(res_info['start_time'])
|
|
106
|
-
args['cancelled_end_time'] = map_arg(res_info['end_time'])
|
|
107
102
|
return self.remove_reservation(res_id=unmap_arg(args['res_id']))
|
|
108
103
|
else:
|
|
109
104
|
return {"ace": map_arg("Invalid reservation ID or user does not own reservation.")}
|
|
@@ -649,14 +644,6 @@ class ReservationHandler:
|
|
|
649
644
|
"total_reservations": int(total_reservations),
|
|
650
645
|
"total_reserved_hours": round(total_reserved_seconds / 3600.0, 1),
|
|
651
646
|
}
|
|
652
|
-
|
|
653
|
-
@db_connection
|
|
654
|
-
def get_reservation_details(self, res_id: int) -> dict | None:
|
|
655
|
-
self.cursor.execute('SELECT device_id, start_time, end_time FROM Reservations WHERE res_id = ?', (res_id,))
|
|
656
|
-
row = self.cursor.fetchone()
|
|
657
|
-
if row:
|
|
658
|
-
return {"device_id": row[0], "start_time": row[1], "end_time": row[2]}
|
|
659
|
-
return None
|
|
660
647
|
|
|
661
648
|
def get_reservation_count(self, username):
|
|
662
649
|
self.cursor.execute("SELECT COUNT(*) FROM Reservations WHERE username = ?", (username,))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: remoteRF-server
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Description-Content-Type: text/markdown
|
|
5
5
|
Requires-Dist: grpcio
|
|
6
6
|
Requires-Dist: protobuf
|
|
@@ -9,6 +9,7 @@ Requires-Dist: prompt_toolkit
|
|
|
9
9
|
Requires-Dist: python-dotenv
|
|
10
10
|
Requires-Dist: grpcio-tools
|
|
11
11
|
Requires-Dist: requests
|
|
12
|
+
Requires-Dist: pyyaml
|
|
12
13
|
|
|
13
14
|
# Remote RF Server Guide (Linux)
|
|
14
15
|
|
|
@@ -14,6 +14,10 @@ src/remoteRF_server/common/grpc/grpc_host_pb2.py
|
|
|
14
14
|
src/remoteRF_server/common/grpc/grpc_host_pb2_grpc.py
|
|
15
15
|
src/remoteRF_server/common/grpc/grpc_pb2.py
|
|
16
16
|
src/remoteRF_server/common/grpc/grpc_pb2_grpc.py
|
|
17
|
+
src/remoteRF_server/common/idl/__init__.py
|
|
18
|
+
src/remoteRF_server/common/idl/device_schema.py
|
|
19
|
+
src/remoteRF_server/common/idl/pluto_schema.py
|
|
20
|
+
src/remoteRF_server/common/idl/schema.py
|
|
17
21
|
src/remoteRF_server/common/utils/__init__.py
|
|
18
22
|
src/remoteRF_server/common/utils/ansi_codes.py
|
|
19
23
|
src/remoteRF_server/common/utils/api_token.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/grpc/__init__.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/grpc/grpc_host_pb2.py
RENAMED
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/grpc/grpc_pb2.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/grpc/grpc_pb2_grpc.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/ansi_codes.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/api_token.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/db_connection.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/list_string.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/common/utils/process_arg.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/host/host_auth_token.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/host/host_directory_store.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/host/host_tunnel_server.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/cert_provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/user_group_cli.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/server/user_group_handler.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server/tools/gist_status_testing.py
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{remoterf_server-0.1.136 → remoterf_server-0.2.0}/src/remoteRF_server.egg-info/top_level.txt
RENAMED
|
File without changes
|