remotivelabs-cli 0.3.0__tar.gz → 0.3.1__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.
Potentially problematic release.
This version of remotivelabs-cli might be problematic. Click here for more details.
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/PKG-INFO +1 -1
- remotivelabs_cli-0.3.1/cli/broker/__init__.py +36 -0
- remotivelabs_cli-0.3.1/cli/broker/discovery.py +43 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/export.py +6 -36
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/files.py +12 -12
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/lib/broker.py +13 -18
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/lib/helper.py +6 -7
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/license_flows.py +11 -13
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/playback.py +10 -10
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/record.py +4 -4
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/scripting.py +6 -9
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/signals.py +17 -19
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/auth/cmd.py +17 -14
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/auth/login.py +16 -26
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/auth_tokens.py +9 -13
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/brokers.py +5 -9
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/configs.py +4 -17
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/organisations.py +8 -10
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/projects.py +3 -3
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/recordings.py +35 -61
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/recordings_playback.py +22 -22
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/resumable_upload.py +6 -6
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/service_account_tokens.py +3 -2
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/storage/cmd.py +2 -3
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/storage/copy.py +2 -1
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/connect/connect.py +4 -4
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/connect/protopie/protopie.py +21 -29
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/remotive.py +4 -7
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/core.py +4 -2
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/migration/migration_tools.py +2 -1
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/tools/can/can.py +4 -7
- remotivelabs_cli-0.3.1/cli/topology/__init__.py +3 -0
- remotivelabs_cli-0.3.1/cli/topology/cmd.py +79 -0
- remotivelabs_cli-0.3.1/cli/topology/start_trial.py +105 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/typer/typer_utils.py +3 -6
- remotivelabs_cli-0.3.1/cli/utils/console.py +61 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/utils/rest_helper.py +23 -16
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/utils/versions.py +2 -4
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/pyproject.toml +1 -1
- remotivelabs_cli-0.3.0/cli/broker/brokers.py +0 -93
- remotivelabs_cli-0.3.0/cli/errors.py +0 -44
- remotivelabs_cli-0.3.0/cli/topology/cmd.py +0 -101
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/LICENSE +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/README.md +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/.DS_Store +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/api/cloud/tokens.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/lib/__about__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/lib/client.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/lib/signalcreator.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/broker/licenses.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/auth/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/licenses/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/licenses/cmd.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/sample_recordings.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/service_accounts.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/storage/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/storage/uri_or_path.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/cloud/uri.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/connect/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/config_file.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/migration/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/migration/migrate_all_token_files.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/migration/migrate_config_file.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/migration/migrate_legacy_dirs.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/migration/migrate_token_file.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/state_file.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/settings/token_file.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/tools/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/tools/can/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/tools/tools.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/typer/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/utils/__init__.py +0 -0
- {remotivelabs_cli-0.3.0 → remotivelabs_cli-0.3.1}/cli/utils/time.py +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from cli.broker.discovery import discover as discover_cmd
|
|
8
|
+
from cli.typer import typer_utils
|
|
9
|
+
|
|
10
|
+
from . import export, files, licenses, playback, record, scripting, signals
|
|
11
|
+
|
|
12
|
+
app = typer_utils.create_typer(rich_markup_mode="rich")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# TODO: remove this?
|
|
16
|
+
def cb(url: str = typer.Option(None, is_eager=False, help="Broker URL", envvar="REMOTIVE_BROKER_URL")) -> None:
|
|
17
|
+
# This can be used to override the --url per command, lets see if this is a better approach
|
|
18
|
+
if url is not None:
|
|
19
|
+
os.environ["REMOTIVE_BROKER_URL"] = url
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# TODO: move broker commands to subcommand instead?
|
|
23
|
+
app.command(help="Discover brokers on this network")(discover_cmd)
|
|
24
|
+
app.callback()(cb)
|
|
25
|
+
|
|
26
|
+
# subcommands
|
|
27
|
+
app.add_typer(playback.app, name="playback", help="Manage playing recordings")
|
|
28
|
+
app.add_typer(record.app, name="record", help="Record data on buses")
|
|
29
|
+
app.add_typer(files.app, name="files", help="Upload/Download configurations and recordings")
|
|
30
|
+
app.add_typer(signals.app, name="signals", help="Find and subscribe to signals")
|
|
31
|
+
app.add_typer(export.app, name="export", help="Export to external formats")
|
|
32
|
+
app.add_typer(scripting.app, name="scripting", help="LUA scripting utilities")
|
|
33
|
+
app.add_typer(licenses.app, name="license", help="View and request license to broker")
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
app()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from time import sleep
|
|
4
|
+
|
|
5
|
+
from zeroconf import IPVersion, ServiceBrowser, ServiceStateChange, Zeroconf
|
|
6
|
+
|
|
7
|
+
from cli.utils.console import print_generic_message, print_newline
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def discover() -> None:
|
|
11
|
+
zeroconf = Zeroconf(ip_version=IPVersion.V4Only)
|
|
12
|
+
services = ["_remotivebroker._tcp.local."]
|
|
13
|
+
|
|
14
|
+
print_generic_message("Looking for RemotiveBrokers on your network, press Ctrl-C to exit...")
|
|
15
|
+
ServiceBrowser(zeroconf, services, handlers=[on_service_state_change])
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
while True:
|
|
19
|
+
sleep(0.1)
|
|
20
|
+
except KeyboardInterrupt:
|
|
21
|
+
pass
|
|
22
|
+
finally:
|
|
23
|
+
zeroconf.close()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def on_service_state_change(zeroconf: Zeroconf, service_type: str, name: str, state_change: ServiceStateChange) -> None:
|
|
27
|
+
"""TODO: use log instead of print for debug information?"""
|
|
28
|
+
if state_change is ServiceStateChange.Removed:
|
|
29
|
+
print_generic_message(f"Service {name} was removed")
|
|
30
|
+
|
|
31
|
+
if state_change is ServiceStateChange.Updated:
|
|
32
|
+
print_generic_message(f"Service {name} was updated")
|
|
33
|
+
|
|
34
|
+
if state_change is ServiceStateChange.Added:
|
|
35
|
+
print_generic_message(f"[ {name} ]")
|
|
36
|
+
info = zeroconf.get_service_info(service_type, name)
|
|
37
|
+
if info:
|
|
38
|
+
for addr in info.parsed_scoped_addresses():
|
|
39
|
+
print_generic_message(f"RemotiveBrokerApp: http://{addr}:8080")
|
|
40
|
+
print_generic_message(f"RemotiveBroker http://{addr}:50051")
|
|
41
|
+
else:
|
|
42
|
+
print_generic_message(" No info")
|
|
43
|
+
print_newline()
|
|
@@ -7,7 +7,7 @@ from typing import Any, List
|
|
|
7
7
|
import grpc
|
|
8
8
|
import typer
|
|
9
9
|
|
|
10
|
-
from cli.
|
|
10
|
+
from cli.utils.console import print_generic_message, print_grpc_error, print_hint
|
|
11
11
|
|
|
12
12
|
from ..typer import typer_utils
|
|
13
13
|
from .lib.broker import Broker, SubscribableSignal
|
|
@@ -26,7 +26,6 @@ def influxdb(
|
|
|
26
26
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
|
27
27
|
api_key: str = typer.Option(None, help="Cloud Broker API-KEY", envvar="REMOTIVE_BROKER_API_KEY"),
|
|
28
28
|
signal: List[str] = typer.Option(..., help="List of signal names to subscribe to in format namespace:signal_name"),
|
|
29
|
-
# namespace: str = typer.Option(..., help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
|
30
29
|
on_change_only: bool = typer.Option(default=False, help="Only get signal if value is changed"),
|
|
31
30
|
output: str = typer.Option(None, help="Write results to file, defaults to stdout"),
|
|
32
31
|
) -> None:
|
|
@@ -51,6 +50,7 @@ def influxdb(
|
|
|
51
50
|
|
|
52
51
|
"""
|
|
53
52
|
|
|
53
|
+
# TODO: file is not properly opened using a context manager
|
|
54
54
|
if output is not None:
|
|
55
55
|
f = open(output, "w")
|
|
56
56
|
|
|
@@ -65,53 +65,23 @@ def influxdb(
|
|
|
65
65
|
return
|
|
66
66
|
sig: str = signals[0]["name"].rpartition(".")[-1]
|
|
67
67
|
frame = signals[0]["name"].rsplit(".", 1)[0]
|
|
68
|
-
# frame_name = signals[0]["name"].split(".")[1]
|
|
69
68
|
namespace = signals[0]["namespace"]
|
|
70
69
|
signals_str = ",".join(list(map(lambda s: f"{sig}={s['value']}", signals)))
|
|
71
70
|
influx_lp = f"{frame},namespace={namespace} {signals_str} {round(signals[0]['timestamp_us'] * 1000)}"
|
|
72
71
|
if output is not None:
|
|
73
72
|
f.write(f"{influx_lp}\n")
|
|
74
73
|
else:
|
|
75
|
-
print
|
|
76
|
-
|
|
77
|
-
# TODO - support for csv
|
|
78
|
-
# def csv(x):
|
|
79
|
-
# list = list(x)
|
|
80
|
-
# print(x)
|
|
81
|
-
# l = list(x)
|
|
82
|
-
# print(l)
|
|
83
|
-
# ll=list(map(lambda s : s, l))
|
|
84
|
-
# for s in l:
|
|
85
|
-
# dt = datetime.fromtimestamp(s["timestamp_nanos"] / 1000000)
|
|
86
|
-
# t=datetime.isoformat(dt)
|
|
87
|
-
# t=rfc3339.format_millisecond(dt)
|
|
88
|
-
# rich_rprint(len(l))
|
|
89
|
-
# lat = (l[0])
|
|
90
|
-
# lon = l[1]
|
|
91
|
-
# dt = datetime.fromtimestamp(lat["timestamp_nanos"] / 1000000)
|
|
92
|
-
# t=datetime.isoformat(dt)
|
|
93
|
-
# t = rfc3339.format_millisecond(dt)
|
|
94
|
-
# name = s["name"]
|
|
95
|
-
# value = s["value"]
|
|
96
|
-
# if output is not None:
|
|
97
|
-
# f.write(f'coord,{lat["value"]},{lon["value"]},{t}\n')
|
|
98
|
-
# else:
|
|
99
|
-
# print(f'coord,{lat["value"]},{lon["value"]},{t}')
|
|
100
|
-
# if output is not None:
|
|
101
|
-
# f.flush()
|
|
102
|
-
# print(x["timestamp_nanos"])
|
|
103
|
-
# rich_rprint(json.dumps(list(x)))
|
|
74
|
+
# TODO: Use log instead of print for debug information?
|
|
75
|
+
print_generic_message(f"{influx_lp}")
|
|
104
76
|
|
|
105
77
|
os_signal.signal(os_signal.SIGINT, exit_on_ctrlc)
|
|
106
78
|
|
|
107
|
-
# print(namespace)
|
|
108
|
-
# signals2 = list(map( lambda s: s['signal'], broker.list_signal_names2(namespace)))
|
|
109
79
|
try:
|
|
110
80
|
|
|
111
81
|
def to_subscribable_signal(sig: str) -> SubscribableSignal:
|
|
112
82
|
arr = sig.split(":")
|
|
113
83
|
if len(arr) != 2:
|
|
114
|
-
|
|
84
|
+
print_hint(f"--signal must have format namespace:signal ({sig})")
|
|
115
85
|
sys.exit(1)
|
|
116
86
|
|
|
117
87
|
return SubscribableSignal(namespace=arr[0], name=arr[1])
|
|
@@ -120,4 +90,4 @@ def influxdb(
|
|
|
120
90
|
broker = Broker(url, api_key)
|
|
121
91
|
broker.long_name_subscribe(signals_to_subscribe_to, per_frame_influx_line_protocol, on_change_only)
|
|
122
92
|
except grpc.RpcError as rpc_error:
|
|
123
|
-
|
|
93
|
+
print_grpc_error(rpc_error)
|
|
@@ -7,8 +7,8 @@ import grpc
|
|
|
7
7
|
import typer
|
|
8
8
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
9
9
|
|
|
10
|
-
from cli.errors import ErrorPrinter
|
|
11
10
|
from cli.typer import typer_utils
|
|
11
|
+
from cli.utils.console import print_generic_error, print_grpc_error, print_success
|
|
12
12
|
|
|
13
13
|
from .lib.broker import Broker
|
|
14
14
|
|
|
@@ -23,9 +23,9 @@ def reload_configuration(
|
|
|
23
23
|
try:
|
|
24
24
|
broker = Broker(url, api_key)
|
|
25
25
|
broker.reload_config()
|
|
26
|
-
|
|
26
|
+
print_success("Configuration reloaded")
|
|
27
27
|
except grpc.RpcError as err:
|
|
28
|
-
|
|
28
|
+
print_grpc_error(err)
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@app.command()
|
|
@@ -42,12 +42,12 @@ def delete(
|
|
|
42
42
|
broker = Broker(url, api_key)
|
|
43
43
|
|
|
44
44
|
if len(path) == 0:
|
|
45
|
-
|
|
45
|
+
print_generic_error("At least one path must be suppled")
|
|
46
46
|
raise typer.Exit(1)
|
|
47
47
|
|
|
48
48
|
broker.delete_files(path, exit_on_failure)
|
|
49
49
|
except grpc.RpcError as err:
|
|
50
|
-
|
|
50
|
+
print_grpc_error(err)
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
@app.command()
|
|
@@ -72,12 +72,12 @@ def download(
|
|
|
72
72
|
if output != "":
|
|
73
73
|
output_file = output
|
|
74
74
|
if os.path.exists(output_file):
|
|
75
|
-
|
|
75
|
+
print_generic_error(f"File already exist {output_file}, please use another output file name")
|
|
76
76
|
else:
|
|
77
77
|
broker.download(path, output_file)
|
|
78
|
-
|
|
78
|
+
print_success(f"{output_file} saved")
|
|
79
79
|
except grpc.RpcError as err:
|
|
80
|
-
|
|
80
|
+
print_grpc_error(err)
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
@app.command()
|
|
@@ -102,19 +102,19 @@ def upload(
|
|
|
102
102
|
path = "./" ## Does not work otherwise
|
|
103
103
|
|
|
104
104
|
if not os.path.exists(path):
|
|
105
|
-
|
|
105
|
+
print_generic_error(f"File {path} does not exist")
|
|
106
106
|
raise typer.Exit(1)
|
|
107
107
|
|
|
108
108
|
broker = Broker(url, api_key)
|
|
109
109
|
|
|
110
110
|
if os.path.isdir(path):
|
|
111
111
|
broker.upload_folder(path)
|
|
112
|
-
|
|
112
|
+
print_success(f"{path} uploaded")
|
|
113
113
|
else:
|
|
114
114
|
output_file = os.path.basename(path)
|
|
115
115
|
if output != "":
|
|
116
116
|
output_file = output
|
|
117
117
|
broker.upload(path, output_file)
|
|
118
|
-
|
|
118
|
+
print_success(f"{path} uploaded")
|
|
119
119
|
except grpc.RpcError as err:
|
|
120
|
-
|
|
120
|
+
print_grpc_error(err)
|
|
@@ -27,14 +27,11 @@ import remotivelabs.broker._generated.traffic_api_pb2 as traffic_api
|
|
|
27
27
|
import remotivelabs.broker._generated.traffic_api_pb2_grpc as traffic_api_grpc
|
|
28
28
|
import typer
|
|
29
29
|
from google.protobuf.json_format import MessageToDict
|
|
30
|
-
from rich.console import Console
|
|
31
30
|
|
|
32
31
|
from cli.broker.lib.helper import act_on_scripted_signal, act_on_signal, create_channel
|
|
33
32
|
from cli.broker.lib.signalcreator import SignalCreator
|
|
34
|
-
from cli.errors import ErrorPrinter
|
|
35
33
|
from cli.settings import settings
|
|
36
|
-
|
|
37
|
-
err_console = Console(stderr=True)
|
|
34
|
+
from cli.utils.console import print_generic_error, print_hint, print_success
|
|
38
35
|
|
|
39
36
|
|
|
40
37
|
@dataclass
|
|
@@ -89,7 +86,7 @@ class Broker:
|
|
|
89
86
|
os.environ["ACCESS_TOKEN"] = "false"
|
|
90
87
|
self.intercept_channel = create_channel(url, None, None)
|
|
91
88
|
else:
|
|
92
|
-
|
|
89
|
+
print_hint("Option --api-key will is deprecated and will be removed. Use access access tokens by logging in with cli.")
|
|
93
90
|
os.environ["ACCESS_TOKEN"] = "false"
|
|
94
91
|
self.intercept_channel = create_channel(url, api_key, None)
|
|
95
92
|
|
|
@@ -103,7 +100,7 @@ class Broker:
|
|
|
103
100
|
err_cnt = 0
|
|
104
101
|
for mode in status.playbackInfo:
|
|
105
102
|
if mode.playbackMode.errorMessage:
|
|
106
|
-
|
|
103
|
+
print_generic_error(mode.playbackMode.errorMessage)
|
|
107
104
|
err_cnt = err_cnt + 1
|
|
108
105
|
if err_cnt > 0:
|
|
109
106
|
raise typer.Exit(1)
|
|
@@ -253,7 +250,7 @@ class Broker:
|
|
|
253
250
|
config = network_api.FramesDistributionConfig(namespace=common.NameSpace(name=namespace))
|
|
254
251
|
frame_distribution_stream = self.network_stub.SubscribeToFramesDistribution(config)
|
|
255
252
|
for frame in frame_distribution_stream:
|
|
256
|
-
f = MessageToDict(frame,
|
|
253
|
+
f = MessageToDict(frame, preserving_proto_field_name=True)
|
|
257
254
|
callback(f)
|
|
258
255
|
|
|
259
256
|
def pause(self, namespace: str, path: str, silent: bool = False) -> traffic_api.PlaybackInfos:
|
|
@@ -331,10 +328,10 @@ class Broker:
|
|
|
331
328
|
|
|
332
329
|
for r in response:
|
|
333
330
|
if r["data"]:
|
|
334
|
-
|
|
331
|
+
print_success(f"Received traffic on {r['namespace']}")
|
|
335
332
|
keep_running = False
|
|
336
333
|
elif not wait_for_traffic or (not keep_running and not keep_running_during_recording):
|
|
337
|
-
|
|
334
|
+
print_generic_error(f"Namespace {r['namespace']} did not receive any traffic")
|
|
338
335
|
|
|
339
336
|
def upload(self, file: str, dest: str) -> None:
|
|
340
337
|
sha256 = get_sha256(file)
|
|
@@ -343,7 +340,7 @@ class Broker:
|
|
|
343
340
|
try:
|
|
344
341
|
self.system_stub.UploadFile(upload_iterator, compression=grpc.Compression.Gzip)
|
|
345
342
|
except grpc.RpcError as rpc_error:
|
|
346
|
-
|
|
343
|
+
print_generic_error(f"Failed to upload file - {rpc_error.details()} ({rpc_error.code()})")
|
|
347
344
|
raise typer.Exit(1)
|
|
348
345
|
|
|
349
346
|
def delete_files(self, path: List[str], exit_on_faliure: bool) -> None:
|
|
@@ -353,7 +350,7 @@ class Broker:
|
|
|
353
350
|
system_api.FileDescriptions(fileDescriptions=[system_api.FileDescription(path=file.replace(ntpath.sep, posixpath.sep))])
|
|
354
351
|
)
|
|
355
352
|
except grpc.RpcError as rpc_error:
|
|
356
|
-
|
|
353
|
+
print_generic_error(f"Failed to delete file - {rpc_error.details()} ({rpc_error.code()})")
|
|
357
354
|
if exit_on_faliure:
|
|
358
355
|
raise typer.Exit(1)
|
|
359
356
|
|
|
@@ -374,7 +371,7 @@ class Broker:
|
|
|
374
371
|
except grpc.RpcError as rpc_error:
|
|
375
372
|
if delegate_err:
|
|
376
373
|
raise rpc_error
|
|
377
|
-
|
|
374
|
+
print_generic_error(f"Failed to download file - {rpc_error.details()} ({rpc_error.code()})")
|
|
378
375
|
# There will be an empty file if the download fails so remove that one here
|
|
379
376
|
os.remove(dest)
|
|
380
377
|
raise typer.Exit(1)
|
|
@@ -384,11 +381,11 @@ class Broker:
|
|
|
384
381
|
request = common.Empty()
|
|
385
382
|
response = self.system_stub.ReloadConfiguration(request, timeout=60000)
|
|
386
383
|
if response.errorMessage:
|
|
387
|
-
|
|
384
|
+
print_generic_error(f"Failed to reload config: {response.errorMessage}")
|
|
388
385
|
raise typer.Exit(1)
|
|
389
386
|
# br.helper.reload_configuration(system_stub=self.system_stub)
|
|
390
387
|
except grpc.RpcError as rpc_error:
|
|
391
|
-
|
|
388
|
+
print_generic_error(f"Failed to reload configuration - {rpc_error.details()} ({rpc_error.code()})")
|
|
392
389
|
raise typer.Exit(1)
|
|
393
390
|
|
|
394
391
|
def list_namespaces(self) -> List[str]:
|
|
@@ -412,7 +409,6 @@ class Broker:
|
|
|
412
409
|
):
|
|
413
410
|
metadata_dict = MessageToDict(
|
|
414
411
|
finfo.signalInfo.metaData,
|
|
415
|
-
including_default_value_fields=True,
|
|
416
412
|
preserving_proto_field_name=True,
|
|
417
413
|
)
|
|
418
414
|
sig_dict = {
|
|
@@ -430,7 +426,6 @@ class Broker:
|
|
|
430
426
|
|
|
431
427
|
metadata_dict = MessageToDict(
|
|
432
428
|
sinfo.metaData,
|
|
433
|
-
including_default_value_fields=True,
|
|
434
429
|
preserving_proto_field_name=True,
|
|
435
430
|
)
|
|
436
431
|
sig_dict = {
|
|
@@ -485,7 +480,7 @@ class Broker:
|
|
|
485
480
|
if ns not in existing_ns:
|
|
486
481
|
ns_not_matching.append(ns)
|
|
487
482
|
if len(ns_not_matching) > 0:
|
|
488
|
-
|
|
483
|
+
print_hint(f"Namespace(s) {ns_not_matching} does not exist on broker. Namespaces found on broker: {existing_ns}")
|
|
489
484
|
sys.exit(1)
|
|
490
485
|
|
|
491
486
|
available_signals = list(filter(verify_namespace, existing_signals)) # type: ignore
|
|
@@ -495,7 +490,7 @@ class Broker:
|
|
|
495
490
|
signals_subscribed_to_but_does_not_exist = set(subscribed_signals) - set(map(lambda s: s["signal"], signals_to_subscribe_to))
|
|
496
491
|
|
|
497
492
|
if len(signals_subscribed_to_but_does_not_exist) > 0:
|
|
498
|
-
|
|
493
|
+
print_hint(f"One or more signals you subscribed to does not exist {signals_subscribed_to_but_does_not_exist}")
|
|
499
494
|
sys.exit(1)
|
|
500
495
|
|
|
501
496
|
return list(map(lambda s: SubscribableSignal(s["signal"], s["namespace"]), signals_to_subscribe_to))
|
|
@@ -18,6 +18,8 @@ import remotivelabs.broker._generated.system_api_pb2 as system_api
|
|
|
18
18
|
import remotivelabs.broker._generated.system_api_pb2_grpc as system_api_grpc
|
|
19
19
|
from grpc_interceptor import ClientCallDetails, ClientInterceptor
|
|
20
20
|
|
|
21
|
+
from cli.utils.console import print_generic_error
|
|
22
|
+
|
|
21
23
|
log = getLogger(__name__)
|
|
22
24
|
|
|
23
25
|
|
|
@@ -221,15 +223,13 @@ def act_on_signal( # noqa: PLR0913
|
|
|
221
223
|
|
|
222
224
|
except grpc.RpcError as e:
|
|
223
225
|
# Only try to cancel if cancel was not already attempted
|
|
224
|
-
# pylint: disable=no-member
|
|
225
226
|
if e.code() != grpc.StatusCode.CANCELLED:
|
|
226
227
|
try:
|
|
227
228
|
subscripton.cancel()
|
|
228
|
-
|
|
229
|
-
|
|
229
|
+
print_generic_error("A gRPC error occurred:")
|
|
230
|
+
print_generic_error(str(e))
|
|
230
231
|
except grpc.RpcError:
|
|
231
232
|
pass
|
|
232
|
-
# pylint: disable=protected-access, bad-except-order
|
|
233
233
|
except grpc._channel._Rendezvous as err: # type:ignore[attr-defined]
|
|
234
234
|
log.error(err)
|
|
235
235
|
# reload, alternatively non-existing signal
|
|
@@ -266,12 +266,11 @@ def act_on_scripted_signal( # noqa: PLR0913
|
|
|
266
266
|
except grpc.RpcError as e:
|
|
267
267
|
try:
|
|
268
268
|
subscription.cancel()
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
print_generic_error("A gRPC error occurred:")
|
|
270
|
+
print_generic_error(str(e))
|
|
271
271
|
except grpc.RpcError:
|
|
272
272
|
pass
|
|
273
273
|
|
|
274
|
-
# pylint: disable=protected-access, bad-except-order
|
|
275
274
|
except grpc._channel._Rendezvous as err: # type:ignore[attr-defined]
|
|
276
275
|
log.error(err)
|
|
277
276
|
# reload, alternatively non-existing signal
|
|
@@ -7,14 +7,12 @@ from typing import Union
|
|
|
7
7
|
import grpc
|
|
8
8
|
import requests
|
|
9
9
|
import typer
|
|
10
|
-
from rich.console import Console
|
|
11
10
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
12
11
|
|
|
13
12
|
from cli.broker.lib.broker import Broker, LicenseInfo
|
|
13
|
+
from cli.utils.console import print_unformatted_to_stderr
|
|
14
14
|
from cli.utils.rest_helper import RestHelper as Rest
|
|
15
15
|
|
|
16
|
-
console = Console(stderr=True)
|
|
17
|
-
|
|
18
16
|
|
|
19
17
|
class LicenseFlow:
|
|
20
18
|
def describe_with_url(self, url: str) -> LicenseInfo:
|
|
@@ -35,7 +33,7 @@ class LicenseFlow:
|
|
|
35
33
|
return b.get_license()
|
|
36
34
|
|
|
37
35
|
def request_with_url_with_internet(self, url: str) -> None:
|
|
38
|
-
|
|
36
|
+
print_unformatted_to_stderr("This requires internet connection from your computer during the entire licensing process")
|
|
39
37
|
|
|
40
38
|
email = LicenseFlow.__try_authenticate_and_get_email_from_cloud()
|
|
41
39
|
|
|
@@ -52,7 +50,7 @@ class LicenseFlow:
|
|
|
52
50
|
if not apply:
|
|
53
51
|
return
|
|
54
52
|
|
|
55
|
-
|
|
53
|
+
print_unformatted_to_stderr(
|
|
56
54
|
":point_right: [bold yellow]This will request a new license or use an existing license if "
|
|
57
55
|
"this hardware has already been licensed[/bold yellow]"
|
|
58
56
|
)
|
|
@@ -66,7 +64,7 @@ class LicenseFlow:
|
|
|
66
64
|
with use_progress("Applying license to broker..."):
|
|
67
65
|
new_license = broker_to_license.apply_license(license_data_b64)
|
|
68
66
|
if new_license.valid:
|
|
69
|
-
|
|
67
|
+
print_unformatted_to_stderr(f":thumbsup: Successfully applied license, it remains valid until {new_license.expires}")
|
|
70
68
|
|
|
71
69
|
def request_with_hotspot(self, url: Union[str, None] = "http://192.168.4.1:50051") -> None:
|
|
72
70
|
"""
|
|
@@ -76,7 +74,7 @@ class LicenseFlow:
|
|
|
76
74
|
if url is None:
|
|
77
75
|
url = "http://192.168.4.1:50051"
|
|
78
76
|
|
|
79
|
-
|
|
77
|
+
print_unformatted_to_stderr(
|
|
80
78
|
"Licensing a broker over its wifi hotspot will require you to switch between internet connectivity "
|
|
81
79
|
"and remotivelabs-xxx wifi hotspot"
|
|
82
80
|
)
|
|
@@ -95,7 +93,7 @@ class LicenseFlow:
|
|
|
95
93
|
if not apply:
|
|
96
94
|
return
|
|
97
95
|
|
|
98
|
-
|
|
96
|
+
print_unformatted_to_stderr(
|
|
99
97
|
":point_right: [bold yellow]This will request a new license or use an existing license if "
|
|
100
98
|
"this hardware has already been licensed[/bold yellow]"
|
|
101
99
|
)
|
|
@@ -110,7 +108,7 @@ class LicenseFlow:
|
|
|
110
108
|
progress_label="Applying license to broker... Make sure to switch back to remotivelabs-xxx wifi hotspot",
|
|
111
109
|
)
|
|
112
110
|
new_license = broker_to_license.apply_license(license_data_b64)
|
|
113
|
-
|
|
111
|
+
print_unformatted_to_stderr(f":thumbsup: Successfully applied license, expires {new_license.expires}")
|
|
114
112
|
|
|
115
113
|
@staticmethod
|
|
116
114
|
def __try_connect_to_broker(url: str, progress_label: str, on_error_progress_label: Union[str, None] = None) -> Broker:
|
|
@@ -127,9 +125,9 @@ class LicenseFlow:
|
|
|
127
125
|
)
|
|
128
126
|
time.sleep(1)
|
|
129
127
|
if on_error_progress_label is None:
|
|
130
|
-
|
|
128
|
+
print_unformatted_to_stderr(f":white_check_mark: {progress_label}")
|
|
131
129
|
else:
|
|
132
|
-
|
|
130
|
+
print_unformatted_to_stderr(f":white_check_mark: {on_error_progress_label}")
|
|
133
131
|
return broker_to_license
|
|
134
132
|
|
|
135
133
|
@staticmethod
|
|
@@ -141,7 +139,7 @@ class LicenseFlow:
|
|
|
141
139
|
break
|
|
142
140
|
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
|
|
143
141
|
time.sleep(1)
|
|
144
|
-
|
|
142
|
+
print_unformatted_to_stderr(":white_check_mark: Fetching user info from cloud... Make sure you are connected to internet")
|
|
145
143
|
if r is None:
|
|
146
144
|
return ""
|
|
147
145
|
return str(r.json()["email"])
|
|
@@ -159,7 +157,7 @@ class LicenseFlow:
|
|
|
159
157
|
break
|
|
160
158
|
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
|
|
161
159
|
time.sleep(1)
|
|
162
|
-
|
|
160
|
+
print_unformatted_to_stderr(f":white_check_mark: {progress_label}")
|
|
163
161
|
return bytes(license_data_b64)
|
|
164
162
|
|
|
165
163
|
|
|
@@ -5,8 +5,8 @@ from typing import Dict, List
|
|
|
5
5
|
import grpc
|
|
6
6
|
import typer
|
|
7
7
|
|
|
8
|
-
from cli.errors import ErrorPrinter
|
|
9
8
|
from cli.typer import typer_utils
|
|
9
|
+
from cli.utils.console import print_generic_error, print_generic_message, print_grpc_error, print_success
|
|
10
10
|
|
|
11
11
|
from .lib.broker import Broker
|
|
12
12
|
|
|
@@ -16,7 +16,7 @@ app = typer_utils.create_typer(help=help)
|
|
|
16
16
|
def recording_and_namespace(recording: str) -> Dict[str, str]:
|
|
17
17
|
splitted = recording.split("::")
|
|
18
18
|
if len(splitted) != 2:
|
|
19
|
-
|
|
19
|
+
print_generic_error("Invalid --recording option, expected file_name::namespace")
|
|
20
20
|
raise typer.Exit(1)
|
|
21
21
|
return {"recording": splitted[0], "namespace": splitted[1]}
|
|
22
22
|
|
|
@@ -39,10 +39,10 @@ def play(
|
|
|
39
39
|
try:
|
|
40
40
|
broker = Broker(url, api_key)
|
|
41
41
|
status = broker.play(rec)
|
|
42
|
-
print
|
|
42
|
+
# TODO: use log instead of print for debug information?
|
|
43
|
+
print_generic_message(str(status))
|
|
43
44
|
except grpc.RpcError as err:
|
|
44
|
-
|
|
45
|
-
# print(status)
|
|
45
|
+
print_grpc_error(err)
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
@app.command()
|
|
@@ -64,9 +64,9 @@ def stop(
|
|
|
64
64
|
try:
|
|
65
65
|
broker = Broker(url, api_key)
|
|
66
66
|
broker.stop_play(rec)
|
|
67
|
-
|
|
67
|
+
print_success("Recording stopped")
|
|
68
68
|
except grpc.RpcError as err:
|
|
69
|
-
|
|
69
|
+
print_grpc_error(err)
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
@app.command()
|
|
@@ -87,9 +87,9 @@ def pause(
|
|
|
87
87
|
try:
|
|
88
88
|
broker = Broker(url, api_key)
|
|
89
89
|
broker.pause_play(rec)
|
|
90
|
-
|
|
90
|
+
print_success("Recording paused")
|
|
91
91
|
except grpc.RpcError as err:
|
|
92
|
-
|
|
92
|
+
print_grpc_error(err)
|
|
93
93
|
|
|
94
94
|
|
|
95
95
|
@app.command()
|
|
@@ -115,4 +115,4 @@ def seek(
|
|
|
115
115
|
broker = Broker(url, api_key)
|
|
116
116
|
broker.seek(rec, int(seconds * 1000000))
|
|
117
117
|
except grpc.RpcError as err:
|
|
118
|
-
|
|
118
|
+
print_grpc_error(err)
|
|
@@ -5,8 +5,8 @@ from typing import List
|
|
|
5
5
|
import grpc
|
|
6
6
|
import typer
|
|
7
7
|
|
|
8
|
-
from cli.errors import ErrorPrinter
|
|
9
8
|
from cli.typer import typer_utils
|
|
9
|
+
from cli.utils.console import print_grpc_error, print_success
|
|
10
10
|
|
|
11
11
|
from .lib.broker import Broker
|
|
12
12
|
|
|
@@ -24,7 +24,7 @@ def start(
|
|
|
24
24
|
broker = Broker(url, api_key)
|
|
25
25
|
broker.record_multiple(namespace, filename)
|
|
26
26
|
except grpc.RpcError as rpc_error:
|
|
27
|
-
|
|
27
|
+
print_grpc_error(rpc_error)
|
|
28
28
|
|
|
29
29
|
|
|
30
30
|
@app.command()
|
|
@@ -37,6 +37,6 @@ def stop(
|
|
|
37
37
|
try:
|
|
38
38
|
broker = Broker(url, api_key)
|
|
39
39
|
broker.stop_multiple(namespace, filename)
|
|
40
|
-
|
|
40
|
+
print_success("Recording stopped")
|
|
41
41
|
except grpc.RpcError as rpc_error:
|
|
42
|
-
|
|
42
|
+
print_grpc_error(rpc_error)
|
|
@@ -5,10 +5,9 @@ from string import Template
|
|
|
5
5
|
from typing import List
|
|
6
6
|
|
|
7
7
|
import typer
|
|
8
|
-
from rich import print as rich_print
|
|
9
8
|
|
|
10
|
-
from cli.errors import ErrorPrinter
|
|
11
9
|
from cli.typer import typer_utils
|
|
10
|
+
from cli.utils.console import print_generic_message, print_hint
|
|
12
11
|
|
|
13
12
|
app = typer_utils.create_typer(
|
|
14
13
|
rich_markup_mode="rich",
|
|
@@ -20,10 +19,9 @@ app = typer_utils.create_typer(
|
|
|
20
19
|
|
|
21
20
|
def write(signal_name: str, script: str) -> None:
|
|
22
21
|
path = f"{signal_name}.lua"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
f
|
|
26
|
-
print(f"Secret token written to {path}")
|
|
22
|
+
with open(path, "w") as f:
|
|
23
|
+
f.write(script)
|
|
24
|
+
print_generic_message(f"Secret token written to {path}")
|
|
27
25
|
|
|
28
26
|
|
|
29
27
|
@app.command("new-script")
|
|
@@ -35,7 +33,7 @@ def new_script(
|
|
|
35
33
|
def to_subscribable_signal(sig: str) -> tuple[str, str]:
|
|
36
34
|
arr = sig.split(":")
|
|
37
35
|
if len(arr) != 2:
|
|
38
|
-
|
|
36
|
+
print_hint(f"--input-signal must have format namespace:signal ({sig})")
|
|
39
37
|
sys.exit(1)
|
|
40
38
|
return arr[0], arr[1]
|
|
41
39
|
|
|
@@ -121,7 +119,6 @@ end
|
|
|
121
119
|
|
|
122
120
|
script = template.substitute(
|
|
123
121
|
local_signals=local_signals,
|
|
124
|
-
# signle_input_signal=signals_to_subscribe_to[0][1], # Take one signal and use signal name in tuple
|
|
125
122
|
subscribe_pattern=subscribe_pattern,
|
|
126
123
|
output_signal=output_signal,
|
|
127
124
|
)
|
|
@@ -129,4 +126,4 @@ end
|
|
|
129
126
|
if save:
|
|
130
127
|
write(output_signal, script)
|
|
131
128
|
else:
|
|
132
|
-
|
|
129
|
+
print_generic_message(script)
|