RemoteRF 0.0.7.45__tar.gz → 0.0.7.47__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.
Files changed (35) hide show
  1. {remoterf-0.0.7.45/src/remoteRF.egg-info → remoterf-0.0.7.47}/PKG-INFO +2 -1
  2. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/setup.py +2 -2
  3. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/config/config_cli.py +17 -22
  4. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/core/app.py +34 -2
  5. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/core/grpc_acc.py +8 -1
  6. remoterf-0.0.7.47/src/remoteRF/core/grpc_client.py +320 -0
  7. {remoterf-0.0.7.45 → remoterf-0.0.7.47/src/remoteRF.egg-info}/PKG-INFO +2 -1
  8. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF.egg-info/requires.txt +1 -0
  9. remoterf-0.0.7.45/src/remoteRF/core/grpc_client.py +0 -102
  10. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/LICENSE +0 -0
  11. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/MANIFEST.in +0 -0
  12. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/README.md +0 -0
  13. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/setup.cfg +0 -0
  14. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/__init__.py +0 -0
  15. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/common/__init__.py +0 -0
  16. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/common/grpc/__init__.py +0 -0
  17. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/common/grpc/grpc_pb2.py +0 -0
  18. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/common/grpc/grpc_pb2_grpc.py +0 -0
  19. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/common/utils/__init__.py +0 -0
  20. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/common/utils/ansi_codes.py +0 -0
  21. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/common/utils/api_token.py +0 -0
  22. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/common/utils/list_string.py +0 -0
  23. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/common/utils/process_arg.py +0 -0
  24. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/config/__init__.py +0 -0
  25. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/config/cert_fetcher.py +0 -0
  26. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/core/__init__.py +0 -0
  27. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/core/acc_login.py +0 -0
  28. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/core/version.py +0 -0
  29. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/drivers/__init__.py +0 -0
  30. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/drivers/adalm_pluto/__init__.py +0 -0
  31. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF/drivers/adalm_pluto/pluto_remote.py +0 -0
  32. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF.egg-info/SOURCES.txt +0 -0
  33. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF.egg-info/dependency_links.txt +0 -0
  34. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF.egg-info/entry_points.txt +0 -0
  35. {remoterf-0.0.7.45 → remoterf-0.0.7.47}/src/remoteRF.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoterf
3
- Version: 0.0.7.45
3
+ Version: 0.0.7.47
4
4
  Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
5
5
  Author: Ethan Ge
6
6
  Author-email: ethoGalaxy@gmail.com
@@ -14,6 +14,7 @@ Requires-Dist: protobuf<6.0.0,>=5.0.0
14
14
  Requires-Dist: numpy
15
15
  Requires-Dist: prompt_toolkit
16
16
  Requires-Dist: python-dotenv
17
+ Requires-Dist: prompt-toolkit
17
18
  Dynamic: author
18
19
  Dynamic: author-email
19
20
  Dynamic: classifier
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
6
6
 
7
7
  setup(
8
8
  name="remoterf",
9
- version="0.0.7.45",
9
+ version="0.0.7.47",
10
10
  author="Ethan Ge",
11
11
  author_email="ethoGalaxy@gmail.com",
12
12
  description="A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.",
@@ -17,7 +17,7 @@ setup(
17
17
  license_file='MIT',
18
18
  include_package_data=True, # Includes files specified in MANIFEST.in
19
19
  install_requires=[
20
- "grpcio==1.71.0", "protobuf>=5.0.0,<6.0.0", "numpy", "prompt_toolkit", "python-dotenv"
20
+ "grpcio==1.71.0", "protobuf>=5.0.0,<6.0.0", "numpy", "prompt_toolkit", "python-dotenv", "prompt-toolkit"
21
21
  ],
22
22
  classifiers=[
23
23
  "Programming Language :: Python :: 3",
@@ -85,7 +85,7 @@ def main() -> None:
85
85
  "Usage:\n"
86
86
  " remoterf-config <host:port>\n\n"
87
87
  "Example:\n"
88
- " remoterf-config 123.45.678.901:12345\n"
88
+ " remoterf-config 164.67.195.207:61005\n"
89
89
  )
90
90
  sys.exit(2)
91
91
 
@@ -95,38 +95,33 @@ def main() -> None:
95
95
  print(f"Error: invalid addr '{args.addr}': {e}", file=sys.stderr)
96
96
  sys.exit(2)
97
97
 
98
- cert_port = args.cert_port if args.cert_port is not None else (grpc_port + 1)
98
+ # Single-arg policy
99
+ cert_port = grpc_port + 1 # cert-provider convention
100
+ profile = "default"
101
+ timeout_sec = 3.0
102
+ overwrite = True
99
103
 
100
- # Destination CA cert file
101
104
  certs_dir = _certs_dir()
102
105
  certs_dir.mkdir(parents=True, exist_ok=True)
103
- ca_out = certs_dir / f"{args.profile}.crt"
104
-
105
- if ca_out.exists() and not (args.overwrite):
106
- # Treat as OK; still (re)write env target.
107
- fetched_ok = True
108
- else:
109
- fetched_ok = fetch_and_save_ca_cert(
110
- host,
111
- cert_port,
112
- out_path=ca_out,
113
- timeout_sec=args.timeout,
114
- overwrite=True,
115
- )
106
+ ca_out = certs_dir / f"{profile}.crt"
107
+
108
+ fetched_ok = fetch_and_save_ca_cert(
109
+ host,
110
+ cert_port,
111
+ out_path=ca_out,
112
+ timeout_sec=timeout_sec,
113
+ overwrite=overwrite,
114
+ )
116
115
 
117
116
  if not fetched_ok:
118
- print(
119
- f"Failed to fetch CA cert from {host}:{cert_port}.\n",
120
- file=sys.stderr
121
- )
117
+ print(f"Failed to fetch CA cert from {host}:{cert_port}.", file=sys.stderr)
122
118
  sys.exit(1)
123
119
 
124
- # Write local .env (or config) with chosen server and CA path
125
120
  env_file = _env_path()
126
121
  _write_env_kv(env_file, {
127
122
  "REMOTERF_ADDR": f"{host}:{grpc_port}",
128
123
  "REMOTERF_CA_CERT": str(ca_out),
129
- "REMOTERF_PROFILE": args.profile,
124
+ "REMOTERF_PROFILE": profile,
130
125
  })
131
126
 
132
127
  print("RemoteRF configured successfully.")
@@ -1,3 +1,4 @@
1
+ from remoteRF.core.grpc_client import handle_admin_command
1
2
  from . import *
2
3
  from ..common.utils import *
3
4
 
@@ -30,6 +31,7 @@ def welcome():
30
31
 
31
32
  account.password = password
32
33
  account.email = input("Email: ") # TODO: Email verification.
34
+ account.enrollment_code = input("Enrollment Code: ")
33
35
  # check if login was valid
34
36
  os.system('cls' if os.name == 'nt' else 'clear')
35
37
 
@@ -59,6 +61,7 @@ def commands():
59
61
  printf("'getdev' ", Sty.MAGENTA, " : View devices", Sty.DEFAULT)
60
62
  printf("'help' or 'h' ", Sty.MAGENTA, " : Show this help message", Sty.DEFAULT)
61
63
  printf("'perms' ", Sty.MAGENTA, " : View permissions", Sty.DEFAULT)
64
+ printf("'enroll' ", Sty.MAGENTA, " : Enroll with an enrollment code", Sty.DEFAULT)
62
65
  printf("'exit' or 'quit' ", Sty.MAGENTA, ": Exit", Sty.DEFAULT)
63
66
  printf("'getres' ", Sty.MAGENTA, " : View all reservations", Sty.DEFAULT)
64
67
  printf("'myres' ", Sty.MAGENTA, " : View my reservations", Sty.DEFAULT)
@@ -66,8 +69,20 @@ def commands():
66
69
  printf("'resdev' ", Sty.MAGENTA, " : Reserve a device", Sty.DEFAULT)
67
70
  # printf("'resdev -n' ", Sty.MAGENTA, "- naive reserve device", Sty.DEFAULT)
68
71
  # printf("'resdev s' ", Sty.MAGENTA, "- Reserve a Device (by single date)", Sty.DEFAULT)
69
- # check if user is admin
70
- # if account.get_perms().results['UC'] == 'Admin':
72
+
73
+ if account.is_admin:
74
+ print()
75
+ printf("Admin Commands:", Sty.BOLD)
76
+ printf("'admin printa' ", Sty.MAGENTA, " : Print all accounts", Sty.DEFAULT)
77
+ printf("'admin printr' ", Sty.MAGENTA, " : Print all reservations", Sty.DEFAULT)
78
+ printf("'admin printp' ", Sty.MAGENTA, " : Print all perms", Sty.DEFAULT)
79
+ printf("'admin printd' ", Sty.MAGENTA, " : Print all devices", Sty.DEFAULT)
80
+ printf("'admin rm a <username>' ", Sty.MAGENTA, " : Remove one account", Sty.DEFAULT)
81
+ printf("'admin rm aa' ", Sty.MAGENTA, " : Remove all accounts", Sty.DEFAULT)
82
+ printf("'admin rm ar' ", Sty.MAGENTA, " : Remove all reservations", Sty.DEFAULT)
83
+ # If you expose set_account remotely:
84
+ printf("'admin setacc <username> <U|P|A> [args...]' ", Sty.MAGENTA, " : Set perms", Sty.DEFAULT)
85
+
71
86
 
72
87
  def clear():
73
88
  os.system('cls' if os.name == 'nt' else 'clear')
@@ -267,6 +282,18 @@ def perms():
267
282
  printf(f'No restrictions on reservation count or duration.', Sty.DEFAULT)
268
283
  else:
269
284
  printf(f"Error: Unknown permission level {results[0]}", Sty.BRIGHT_RED)
285
+
286
+ def enroll():
287
+ code = session.prompt(stylize("Enter your enrollment code: ", Sty.DEFAULT))
288
+ account.enrollment_code = code
289
+ data = account.set_enroll()
290
+
291
+ if 'ace' in data.results:
292
+ print(f"Error: {unmap_arg(data.results['ace'])}")
293
+ return
294
+ else:
295
+ # todo: print out specifics, like group name (ie: successfully enrolled in ECE 132A)
296
+ printf("Enrollment successful.", Sty.BOLD_GREEN)
270
297
 
271
298
  # New block scheduling
272
299
 
@@ -484,6 +511,8 @@ while True:
484
511
  commands()
485
512
  elif inpu == "perms":
486
513
  perms()
514
+ elif inpu == "enroll":
515
+ enroll()
487
516
  elif inpu == "quit" or inpu == "exit":
488
517
  break
489
518
  elif inpu == "getres":
@@ -496,10 +525,13 @@ while True:
496
525
  interactive_reserve_next_days(block_minutes=30)
497
526
  elif inpu == 'cancelres':
498
527
  cancel_my_reservation()
528
+
499
529
  elif inpu == 'resdev -n':
500
530
  # check if user is admin
501
531
  # if account.get_perms().results['UC'] == 'Admin':
502
532
  reserve()
533
+ elif account.is_admin and inpu.strip().startswith("admin"):
534
+ handle_admin_command(inpu)
503
535
  else:
504
536
  print(f"Unknown command: {inpu}")
505
537
  except KeyboardInterrupt:
@@ -1,3 +1,4 @@
1
+ import ast
1
2
  from .grpc_client import rpc_client
2
3
  from ..common.utils import *
3
4
 
@@ -8,9 +9,10 @@ class RemoteRFAccount:
8
9
  self.username = username
9
10
  self.password = password
10
11
  self.email = email
12
+ self.enrollment_code = ""
11
13
 
12
14
  def create_user(self):
13
- response = rpc_client(function_name="ACC:create_user", args={"un":map_arg(self.username), "pw":map_arg(self.password), "em":map_arg(self.email)})
15
+ response = rpc_client(function_name="ACC:create_user", args={"un":map_arg(self.username), "pw":map_arg(self.password), "em":map_arg(self.email), "ec":map_arg(self.enrollment_code)})
14
16
  if 'UC' in response.results:
15
17
  print(f'User {unmap_arg(response.results["UC"])} successfully created.')
16
18
  return True
@@ -24,6 +26,8 @@ class RemoteRFAccount:
24
26
  response = rpc_client(function_name="ACC:login", args={"un":map_arg(username), "pw":map_arg(password)})
25
27
  if 'UC' in response.results:
26
28
  print(f'User {unmap_arg(response.results["UC"])} successful login.')
29
+
30
+ self.is_admin = (ast.literal_eval(unmap_arg(self.get_perms().results['UC']))[0][0] == 'Admin')
27
31
  return True
28
32
  elif 'UE' in response.results:
29
33
  print(f'Error: {unmap_arg(response.results["UE"])}')
@@ -49,4 +53,7 @@ class RemoteRFAccount:
49
53
  def get_perms(self):
50
54
  return rpc_client(function_name='ACC:get_perms', args={"un":map_arg(self.username), "pw":map_arg(self.password)})
51
55
 
56
+ def set_enroll(self):
57
+ return rpc_client(function_name='ACC:set_enroll', args={"un":map_arg(self.username), "pw":map_arg(self.password), "ec":map_arg(self.enrollment_code)})
58
+
52
59
 
@@ -0,0 +1,320 @@
1
+ import socket
2
+ import getpass
3
+ from pathlib import Path
4
+ import os
5
+ from dotenv import load_dotenv
6
+
7
+ import grpc
8
+ from ..common.grpc import grpc_pb2
9
+ from ..common.grpc import grpc_pb2_grpc
10
+ from ..common.utils import *
11
+
12
+ load_dotenv(Path.home() / ".config" / "remoterf" / ".env")
13
+ addr = os.getenv("REMOTERF_ADDR") # "host:port"
14
+ ca_path = os.getenv("REMOTERF_CA_CERT") # path to saved CA cert
15
+
16
+ options = [
17
+ ('grpc.max_send_message_length', 100 * 1024 * 1024),
18
+ ('grpc.max_receive_message_length', 100 * 1024 * 1024),
19
+ ]
20
+
21
+ # Server.crt
22
+ certs_path = Path(ca_path).expanduser().resolve()
23
+ with certs_path.open('rb') as f:
24
+ trusted_certs = f.read()
25
+
26
+ credentials = grpc.ssl_channel_credentials(root_certificates=trusted_certs)
27
+ channel = grpc.secure_channel(addr, credentials, options=options)
28
+ stub = grpc_pb2_grpc.GenericRPCStub(channel)
29
+
30
+ tcp_calls = 0
31
+
32
+ def get_tcp_calls():
33
+ return tcp_calls
34
+
35
+ def rpc_client(*, function_name, args):
36
+ global tcp_calls
37
+ tcp_calls += 1
38
+ # print(tcp_calls)
39
+ # if not is_connected:
40
+ # response = rpc_client(function_name="UserLogin", args={"username": grpc_pb2.Argument(string_value=input("Username: ")), "password": grpc_pb2.Argument(string_value=getpass.getpass("Password: ")), "client_ip": grpc_pb2.Argument(string_value=local_ip)})
41
+ # if (response.results['status'].string_value == 'Success'):
42
+ # print("Login successful.")
43
+ # is_connected = True
44
+
45
+ # TODO: Handle user login
46
+
47
+ # print(f"Opening connection to {server_ip}:{server_port}")
48
+
49
+ # TODO: Handle Errors
50
+
51
+ # print(f"Calling function: {function_name}")
52
+ response = stub.Call(grpc_pb2.GenericRPCRequest(function_name=function_name, args=args))
53
+
54
+ if 'a' in response.results:
55
+ print(f"Error: {unmap_arg(response.results['a'])}")
56
+ exit()
57
+
58
+ if 'UE' in response.results:
59
+ print(f"UserError: {unmap_arg(response.results['UE'])}")
60
+ input("Hit enter to continue...")
61
+
62
+ if 'Message' in response.results:
63
+ print(f"{unmap_arg(response.results['Message'])}")
64
+
65
+
66
+ return response
67
+
68
+ #region Example Usage
69
+
70
+ # if __name__ == '__main__':
71
+ # args={
72
+ # "key1": grpc_pb2.Argument(string_value="Hello"),
73
+ # "key2": grpc_pb2.Argument(int32_value=123),
74
+ # "key3": grpc_pb2.Argument(float_value=4.56),
75
+ # "key4": grpc_pb2.Argument(bool_value=True),
76
+ # "client_ip": grpc_pb2.Argument(string_value=local_ip)
77
+ # }
78
+
79
+ # response = rpc_client(function_name="echo", args=args)
80
+
81
+ # if 'client_ip' in response.results:
82
+ # del response.results['client_ip']
83
+
84
+ # # Print results
85
+ # print("Received response:")
86
+ # for key, arg in response.results.items():
87
+ # # Decode the oneof fields
88
+ # if arg.HasField('string_value'):
89
+ # value = arg.string_value
90
+ # elif arg.HasField('int32_value'):
91
+ # value = arg.int32_value
92
+ # elif arg.HasField('float_value'):
93
+ # value = arg.float_value
94
+ # elif arg.HasField('bool_value'):
95
+ # value = arg.bool_value
96
+ # else:
97
+ # value = "Undefined"
98
+
99
+ # print(f"{key}: {value}")
100
+
101
+ #endregion
102
+
103
+ from typing import Any, Dict, Optional
104
+
105
+ def handle_admin_command(inpu: str):
106
+ """
107
+ Parses commands like:
108
+ admin help
109
+ admin printa | printr | printp | printd
110
+ admin rm aa | rm ar | rm a <username>
111
+ admin setacc <username> <U|P|A> [devices=1,2,3] [max_res=5] [max_time=1800]
112
+
113
+ Requires account.is_admin == True (client-side gate). Server still enforces.
114
+ """
115
+ if not getattr(account, "is_admin", False):
116
+ print("Access denied: you are not an Admin.")
117
+ return
118
+
119
+ tokens = (inpu or "").strip().split()
120
+ if len(tokens) < 2 or tokens[0].lower() != "admin":
121
+ print("Usage: admin <command>. Try: admin help")
122
+ return
123
+
124
+ cmd = tokens[1].lower()
125
+
126
+ def call_admin(fn: str, extra: dict | None = None):
127
+ return remote_admin_rpc_client(
128
+ function_name=fn,
129
+ auth_un=account.username,
130
+ auth_pw=account.password,
131
+ args=extra or {},
132
+ print_result=True,
133
+ )
134
+
135
+ if cmd in ("help", "h"):
136
+ print("Admin commands:")
137
+ print(" admin printa - Print all accounts")
138
+ print(" admin printr - Print all reservations")
139
+ print(" admin printp - Print all perms")
140
+ print(" admin printd - Print all devices")
141
+ print(" admin rm aa - Remove all accounts")
142
+ print(" admin rm ar - Remove all reservations")
143
+ print(" admin rm a <username> - Remove one account")
144
+ print(" admin setacc <u> <U|P|A> [devices=1,2] [max_res=3] [max_time=1800]")
145
+ return
146
+
147
+ # ----- print* -----
148
+ if cmd in ("printa", "print_accounts", "print_all_accounts"):
149
+ call_admin("print_all_accounts")
150
+ return
151
+
152
+ if cmd in ("printr", "print_res", "print_all_reservations"):
153
+ call_admin("print_all_reservations")
154
+ return
155
+
156
+ if cmd in ("printp", "print_perms", "print_all_perms"):
157
+ call_admin("print_all_perms")
158
+ return
159
+
160
+ if cmd in ("printd", "print_devices", "print_all_devices"):
161
+ call_admin("print_all_devices")
162
+ return
163
+
164
+ # ----- rm -----
165
+ if cmd == "rm":
166
+ if len(tokens) < 3:
167
+ print("Usage: admin rm <aa|ar|a> [username]")
168
+ return
169
+
170
+ sub = tokens[2].lower()
171
+
172
+ if sub == "aa":
173
+ call_admin("remove_all_users")
174
+ return
175
+
176
+ if sub == "ar":
177
+ call_admin("remove_all_reservations")
178
+ return
179
+
180
+ if sub == "a":
181
+ if len(tokens) < 4:
182
+ print("Usage: admin rm a <username>")
183
+ return
184
+ call_admin("remove_user", {"username": tokens[3]})
185
+ return
186
+
187
+ print("Unknown rm subcommand. Use: aa, ar, a <username>")
188
+ return
189
+
190
+ # ----- setacc -----
191
+ if cmd in ("setacc", "set_account", "setperm", "set"):
192
+ if len(tokens) < 4:
193
+ print("Usage: admin setacc <username> <U|P|A> [devices=1,2] [max_res=3] [max_time=1800]")
194
+ return
195
+
196
+ target_user = tokens[2]
197
+ perm = tokens[3].upper()
198
+
199
+ extra: dict = {"username": target_user, "permission": perm}
200
+
201
+ # Parse optional k=v args (very lightweight parser)
202
+ # Examples:
203
+ # devices=1,2,3
204
+ # max_res=5
205
+ # max_time=1800
206
+ for t in tokens[4:]:
207
+ if "=" not in t:
208
+ continue
209
+ k, v = t.split("=", 1)
210
+ k = k.strip().lower()
211
+ v = v.strip()
212
+
213
+ if k in ("devices", "device_list", "devices_allowed", "device_ids"):
214
+ # server-side remote_admin_call already supports "1,2,3" as string
215
+ extra["device_list"] = v
216
+ elif k in ("max_res", "max_reservations"):
217
+ extra["max_reservations"] = int(v)
218
+ elif k in ("max_time", "max_res_time", "max_reservation_time_sec"):
219
+ extra["max_reservation_time_sec"] = int(v)
220
+
221
+ call_admin("set_account", extra)
222
+ return
223
+
224
+ print(f"Unknown admin command: {cmd}. Try: admin help")
225
+
226
+
227
+ def remote_admin_rpc_client(
228
+ *,
229
+ function_name: str,
230
+ auth_un: str,
231
+ auth_pw: str,
232
+ args: Optional[Dict[str, Any]] = None,
233
+ raise_on_error: bool = False,
234
+ print_result: bool = False,
235
+ ) -> Dict[str, Any]:
236
+ """
237
+ Client helper for RemoteAdmin:<function> calls.
238
+
239
+ Builds and sends:
240
+ function_name = "RemoteAdmin:<function_name>"
241
+ args include:
242
+ auth_un, auth_pw (mapped via map_arg)
243
+ + any additional args (mapped via map_arg if not already grpc_pb2.Argument)
244
+
245
+ Returns:
246
+ {
247
+ "ok": bool,
248
+ "result": str | None,
249
+ "error": str | None,
250
+ "traceback": str | None,
251
+ "raw": grpc_pb2.GenericRPCResponse,
252
+ }
253
+ """
254
+ fn = (function_name or "").strip()
255
+ if fn.startswith("RemoteAdmin:"):
256
+ rpc_fn = fn
257
+ else:
258
+ rpc_fn = f"RemoteAdmin:{fn}"
259
+
260
+ payload: Dict[str, Any] = {
261
+ "auth_un": map_arg(auth_un),
262
+ "auth_pw": map_arg(auth_pw),
263
+ }
264
+
265
+ if args:
266
+ for k, v in args.items():
267
+ if v is None:
268
+ continue
269
+ # If caller already passed a grpc Argument, keep it.
270
+ if isinstance(v, grpc_pb2.Argument):
271
+ payload[str(k)] = v
272
+ else:
273
+ payload[str(k)] = map_arg(v)
274
+
275
+ resp = rpc_client(function_name=rpc_fn, args=payload)
276
+
277
+ # Parse the standardized RemoteAdmin response keys:
278
+ # Ok, Result, Error, Traceback (each typically map_arg-encoded)
279
+ results = getattr(resp, "results", {}) or {}
280
+
281
+ def _get_str(key: str) -> Optional[str]:
282
+ if key not in results:
283
+ return None
284
+ try:
285
+ return str(unmap_arg(results[key]))
286
+ except Exception:
287
+ try:
288
+ return str(results[key])
289
+ except Exception:
290
+ return None
291
+
292
+ ok_val = results.get("Ok", None)
293
+ ok = False
294
+ if ok_val is not None:
295
+ try:
296
+ ok = bool(unmap_arg(ok_val))
297
+ except Exception:
298
+ ok = False
299
+
300
+ out = {
301
+ "ok": ok,
302
+ "result": _get_str("Result"),
303
+ "error": _get_str("Error"),
304
+ "traceback": _get_str("Traceback"),
305
+ "raw": resp,
306
+ }
307
+
308
+ if print_result:
309
+ if out["ok"]:
310
+ print(out["result"] or "OK")
311
+ else:
312
+ print(out["error"] or "RemoteAdmin call failed.")
313
+ if out["traceback"]:
314
+ print(out["traceback"])
315
+
316
+ if raise_on_error and not out["ok"]:
317
+ msg = out["error"] or "RemoteAdmin call failed."
318
+ raise RuntimeError(msg)
319
+
320
+ return out
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: remoterf
3
- Version: 0.0.7.45
3
+ Version: 0.0.7.47
4
4
  Summary: A python API to remotely access signal centric hardware. Client-side only! Courtesy of Wireless Lab @ UCLA & Prof. Ian Roberts.
5
5
  Author: Ethan Ge
6
6
  Author-email: ethoGalaxy@gmail.com
@@ -14,6 +14,7 @@ Requires-Dist: protobuf<6.0.0,>=5.0.0
14
14
  Requires-Dist: numpy
15
15
  Requires-Dist: prompt_toolkit
16
16
  Requires-Dist: python-dotenv
17
+ Requires-Dist: prompt-toolkit
17
18
  Dynamic: author
18
19
  Dynamic: author-email
19
20
  Dynamic: classifier
@@ -3,3 +3,4 @@ protobuf<6.0.0,>=5.0.0
3
3
  numpy
4
4
  prompt_toolkit
5
5
  python-dotenv
6
+ prompt-toolkit
@@ -1,102 +0,0 @@
1
- import socket
2
- import getpass
3
- from pathlib import Path
4
- import os
5
- from dotenv import load_dotenv
6
-
7
- import grpc
8
- from ..common.grpc import grpc_pb2
9
- from ..common.grpc import grpc_pb2_grpc
10
- from ..common.utils import *
11
-
12
- load_dotenv(Path.home() / ".config" / "remoterf" / ".env")
13
- addr = os.getenv("REMOTERF_ADDR") # "host:port"
14
- ca_path = os.getenv("REMOTERF_CA_CERT") # path to saved CA cert
15
-
16
- options = [
17
- ('grpc.max_send_message_length', 100 * 1024 * 1024),
18
- ('grpc.max_receive_message_length', 100 * 1024 * 1024),
19
- ]
20
-
21
- # Server.crt
22
- certs_path = Path(ca_path).expanduser().resolve()
23
- with certs_path.open('rb') as f:
24
- trusted_certs = f.read()
25
-
26
- credentials = grpc.ssl_channel_credentials(root_certificates=trusted_certs)
27
- channel = grpc.secure_channel(addr, credentials, options=options)
28
- stub = grpc_pb2_grpc.GenericRPCStub(channel)
29
-
30
- tcp_calls = 0
31
-
32
- def get_tcp_calls():
33
- return tcp_calls
34
-
35
- def rpc_client(*, function_name, args):
36
- global tcp_calls
37
- tcp_calls += 1
38
- # print(tcp_calls)
39
- # if not is_connected:
40
- # response = rpc_client(function_name="UserLogin", args={"username": grpc_pb2.Argument(string_value=input("Username: ")), "password": grpc_pb2.Argument(string_value=getpass.getpass("Password: ")), "client_ip": grpc_pb2.Argument(string_value=local_ip)})
41
- # if (response.results['status'].string_value == 'Success'):
42
- # print("Login successful.")
43
- # is_connected = True
44
-
45
- # TODO: Handle user login
46
-
47
- # print(f"Opening connection to {server_ip}:{server_port}")
48
-
49
- # TODO: Handle Errors
50
-
51
- # print(f"Calling function: {function_name}")
52
- response = stub.Call(grpc_pb2.GenericRPCRequest(function_name=function_name, args=args))
53
-
54
- if 'a' in response.results:
55
- print(f"Error: {unmap_arg(response.results['a'])}")
56
- exit()
57
-
58
- if 'UE' in response.results:
59
- print(f"UserError: {unmap_arg(response.results['UE'])}")
60
- input("Hit enter to continue...")
61
-
62
- if 'Message' in response.results:
63
- print(f"{unmap_arg(response.results['Message'])}")
64
-
65
-
66
- return response
67
-
68
- #region Example Usage
69
-
70
- # if __name__ == '__main__':
71
- # args={
72
- # "key1": grpc_pb2.Argument(string_value="Hello"),
73
- # "key2": grpc_pb2.Argument(int32_value=123),
74
- # "key3": grpc_pb2.Argument(float_value=4.56),
75
- # "key4": grpc_pb2.Argument(bool_value=True),
76
- # "client_ip": grpc_pb2.Argument(string_value=local_ip)
77
- # }
78
-
79
- # response = rpc_client(function_name="echo", args=args)
80
-
81
- # if 'client_ip' in response.results:
82
- # del response.results['client_ip']
83
-
84
- # # Print results
85
- # print("Received response:")
86
- # for key, arg in response.results.items():
87
- # # Decode the oneof fields
88
- # if arg.HasField('string_value'):
89
- # value = arg.string_value
90
- # elif arg.HasField('int32_value'):
91
- # value = arg.int32_value
92
- # elif arg.HasField('float_value'):
93
- # value = arg.float_value
94
- # elif arg.HasField('bool_value'):
95
- # value = arg.bool_value
96
- # else:
97
- # value = "Undefined"
98
-
99
- # print(f"{key}: {value}")
100
-
101
- #endregion
102
-
File without changes
File without changes
File without changes
File without changes