remotivelabs-cli 0.5.0a1__py3-none-any.whl

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 (84) hide show
  1. remotivelabs/cli/__init__.py +0 -0
  2. remotivelabs/cli/api/cloud/tokens.py +62 -0
  3. remotivelabs/cli/broker/__init__.py +33 -0
  4. remotivelabs/cli/broker/defaults.py +1 -0
  5. remotivelabs/cli/broker/discovery.py +43 -0
  6. remotivelabs/cli/broker/export.py +92 -0
  7. remotivelabs/cli/broker/files.py +119 -0
  8. remotivelabs/cli/broker/lib/__about__.py +4 -0
  9. remotivelabs/cli/broker/lib/broker.py +625 -0
  10. remotivelabs/cli/broker/lib/client.py +224 -0
  11. remotivelabs/cli/broker/lib/helper.py +277 -0
  12. remotivelabs/cli/broker/lib/signalcreator.py +196 -0
  13. remotivelabs/cli/broker/license_flows.py +167 -0
  14. remotivelabs/cli/broker/licenses.py +98 -0
  15. remotivelabs/cli/broker/playback.py +117 -0
  16. remotivelabs/cli/broker/record.py +41 -0
  17. remotivelabs/cli/broker/recording_session/__init__.py +3 -0
  18. remotivelabs/cli/broker/recording_session/client.py +67 -0
  19. remotivelabs/cli/broker/recording_session/cmd.py +254 -0
  20. remotivelabs/cli/broker/recording_session/time.py +49 -0
  21. remotivelabs/cli/broker/scripting.py +129 -0
  22. remotivelabs/cli/broker/signals.py +220 -0
  23. remotivelabs/cli/broker/version.py +31 -0
  24. remotivelabs/cli/cloud/__init__.py +17 -0
  25. remotivelabs/cli/cloud/auth/__init__.py +3 -0
  26. remotivelabs/cli/cloud/auth/cmd.py +128 -0
  27. remotivelabs/cli/cloud/auth/login.py +283 -0
  28. remotivelabs/cli/cloud/auth_tokens.py +149 -0
  29. remotivelabs/cli/cloud/brokers.py +109 -0
  30. remotivelabs/cli/cloud/configs.py +109 -0
  31. remotivelabs/cli/cloud/licenses/__init__.py +0 -0
  32. remotivelabs/cli/cloud/licenses/cmd.py +14 -0
  33. remotivelabs/cli/cloud/organisations.py +112 -0
  34. remotivelabs/cli/cloud/projects.py +44 -0
  35. remotivelabs/cli/cloud/recordings.py +580 -0
  36. remotivelabs/cli/cloud/recordings_playback.py +274 -0
  37. remotivelabs/cli/cloud/resumable_upload.py +87 -0
  38. remotivelabs/cli/cloud/sample_recordings.py +25 -0
  39. remotivelabs/cli/cloud/service_account_tokens.py +62 -0
  40. remotivelabs/cli/cloud/service_accounts.py +72 -0
  41. remotivelabs/cli/cloud/storage/__init__.py +5 -0
  42. remotivelabs/cli/cloud/storage/cmd.py +76 -0
  43. remotivelabs/cli/cloud/storage/copy.py +86 -0
  44. remotivelabs/cli/cloud/storage/uri_or_path.py +45 -0
  45. remotivelabs/cli/cloud/uri.py +113 -0
  46. remotivelabs/cli/connect/__init__.py +0 -0
  47. remotivelabs/cli/connect/connect.py +118 -0
  48. remotivelabs/cli/connect/protopie/protopie.py +185 -0
  49. remotivelabs/cli/py.typed +0 -0
  50. remotivelabs/cli/remotive.py +123 -0
  51. remotivelabs/cli/settings/__init__.py +20 -0
  52. remotivelabs/cli/settings/config_file.py +113 -0
  53. remotivelabs/cli/settings/core.py +333 -0
  54. remotivelabs/cli/settings/migration/__init__.py +0 -0
  55. remotivelabs/cli/settings/migration/migrate_all_token_files.py +80 -0
  56. remotivelabs/cli/settings/migration/migrate_config_file.py +64 -0
  57. remotivelabs/cli/settings/migration/migrate_legacy_dirs.py +50 -0
  58. remotivelabs/cli/settings/migration/migrate_token_file.py +52 -0
  59. remotivelabs/cli/settings/migration/migration_tools.py +38 -0
  60. remotivelabs/cli/settings/state_file.py +67 -0
  61. remotivelabs/cli/settings/token_file.py +128 -0
  62. remotivelabs/cli/tools/__init__.py +0 -0
  63. remotivelabs/cli/tools/can/__init__.py +0 -0
  64. remotivelabs/cli/tools/can/can.py +78 -0
  65. remotivelabs/cli/tools/tools.py +9 -0
  66. remotivelabs/cli/topology/__init__.py +28 -0
  67. remotivelabs/cli/topology/all.py +322 -0
  68. remotivelabs/cli/topology/cli/__init__.py +3 -0
  69. remotivelabs/cli/topology/cli/run_in_docker.py +58 -0
  70. remotivelabs/cli/topology/cli/topology_cli.py +16 -0
  71. remotivelabs/cli/topology/cmd.py +130 -0
  72. remotivelabs/cli/topology/start_trial.py +134 -0
  73. remotivelabs/cli/typer/__init__.py +0 -0
  74. remotivelabs/cli/typer/typer_utils.py +27 -0
  75. remotivelabs/cli/utils/__init__.py +0 -0
  76. remotivelabs/cli/utils/console.py +99 -0
  77. remotivelabs/cli/utils/rest_helper.py +369 -0
  78. remotivelabs/cli/utils/time.py +11 -0
  79. remotivelabs/cli/utils/versions.py +120 -0
  80. remotivelabs_cli-0.5.0a1.dist-info/METADATA +51 -0
  81. remotivelabs_cli-0.5.0a1.dist-info/RECORD +84 -0
  82. remotivelabs_cli-0.5.0a1.dist-info/WHEEL +4 -0
  83. remotivelabs_cli-0.5.0a1.dist-info/entry_points.txt +3 -0
  84. remotivelabs_cli-0.5.0a1.dist-info/licenses/LICENSE +17 -0
File without changes
@@ -0,0 +1,62 @@
1
+ import dataclasses
2
+ from typing import Any
3
+
4
+ import requests
5
+
6
+ from remotivelabs.cli.utils.rest_helper import RestHelper as Rest
7
+
8
+ #
9
+ # Attempt to start cloud-api.
10
+ # Might be better to generate somehow instead.
11
+ # Lets discuss further
12
+ #
13
+
14
+
15
+ @dataclasses.dataclass
16
+ class EmptyResponse:
17
+ is_success: bool
18
+ status_code: int
19
+
20
+
21
+ @dataclasses.dataclass
22
+ class JsonResponse:
23
+ is_success: bool
24
+ status_code: int
25
+ __response: requests.Response
26
+
27
+ def json(self) -> Any:
28
+ return self.__response.json()
29
+
30
+ def text(self) -> str:
31
+ return self.__response.text
32
+
33
+
34
+ def __delete_response_from_response(response: requests.Response) -> EmptyResponse:
35
+ if 200 > response.status_code > 299:
36
+ ok = False
37
+ else:
38
+ ok = True
39
+ return EmptyResponse(is_success=ok, status_code=response.status_code)
40
+
41
+
42
+ def __json_response_from_response(response: requests.Response) -> JsonResponse:
43
+ if 200 > response.status_code > 299:
44
+ ok = False
45
+ else:
46
+ ok = True
47
+ return JsonResponse(is_success=ok, status_code=response.status_code, __response=response)
48
+
49
+
50
+ def create() -> JsonResponse:
51
+ response = Rest.handle_post(url="/api/me/keys", return_response=True)
52
+ return __json_response_from_response(response)
53
+
54
+
55
+ def revoke(name: str) -> EmptyResponse:
56
+ res_revoke = Rest.handle_patch(f"/api/me/keys/{name}/revoke", quiet=True, allow_status_codes=[404, 401])
57
+ return __delete_response_from_response(res_revoke)
58
+
59
+
60
+ def delete(name: str) -> EmptyResponse:
61
+ res_delete = Rest.handle_delete(f"/api/me/keys/{name}", quiet=True, allow_status_codes=[404, 401])
62
+ return __delete_response_from_response(res_delete)
@@ -0,0 +1,33 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ import typer
6
+
7
+ from remotivelabs.cli.broker import export, files, licenses, record, recording_session, scripting, signals
8
+ from remotivelabs.cli.broker.discovery import discover as discover_cmd
9
+ from remotivelabs.cli.typer import typer_utils
10
+
11
+ app = typer_utils.create_typer(rich_markup_mode="rich")
12
+
13
+
14
+ # TODO: remove this?
15
+ def cb(url: str = typer.Option(None, is_eager=False, help="Broker URL", envvar="REMOTIVE_BROKER_URL")) -> None:
16
+ # This can be used to override the --url per command, lets see if this is a better approach
17
+ if url is not None:
18
+ os.environ["REMOTIVE_BROKER_URL"] = url
19
+
20
+
21
+ # TODO: move broker commands to subcommand instead?
22
+ app.command(help="Discover brokers on this network")(discover_cmd)
23
+ app.callback()(cb)
24
+
25
+ # subcommands
26
+
27
+ app.add_typer(recording_session.app, name="playback")
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")
@@ -0,0 +1 @@
1
+ DEFAULT_GRPC_URL = "http://localhost:50051"
@@ -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 remotivelabs.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()
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ import signal as os_signal
4
+ import sys
5
+ from typing import Any, List
6
+
7
+ import grpc
8
+ import typer
9
+
10
+ from remotivelabs.cli.broker.lib.broker import Broker, SubscribableSignal
11
+ from remotivelabs.cli.typer import typer_utils
12
+ from remotivelabs.cli.utils.console import print_generic_message, print_grpc_error, print_hint
13
+
14
+ app = typer_utils.create_typer(
15
+ rich_markup_mode="rich",
16
+ help="""
17
+ Export subscribed signals to different formats, currently only InfluxDB line protocol
18
+ but more formats will come soon
19
+ """,
20
+ )
21
+
22
+
23
+ @app.command()
24
+ def influxdb(
25
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
26
+ api_key: str = typer.Option(None, help="Cloud Broker API-KEY", envvar="REMOTIVE_BROKER_API_KEY"),
27
+ signal: List[str] = typer.Option(..., help="List of signal names to subscribe to in format namespace:signal_name"),
28
+ on_change_only: bool = typer.Option(default=False, help="Only get signal if value is changed"),
29
+ output: str = typer.Option(None, help="Write results to file, defaults to stdout"),
30
+ ) -> None:
31
+ """
32
+ Exports subscribed signals to InfluxDB line-protocol, really useful to dump some signals into
33
+ influxdb for offline analysis and insights.
34
+
35
+ This is a sample for exporting and importing to InfluxDB using remotive-cli and influx-cli
36
+
37
+ Export:
38
+ remotive broker export influxdb --url [URL] --output signals.influx \\
39
+ --signal vehiclebus:Control.SteeringWheel_Position --signal Control.Accelerator_PedalPosition \\
40
+ --signal vehiclebus:GpsPosition.GPS_Longitude --signal vehiclebus:GpsPosition.GPS_Latitude
41
+
42
+ Output:
43
+ Control, namespace:vehiclebus SteeringWheel_Position=1.0,Accelerator_PedalPosition=0,Speed=0 1664787032944374000
44
+ GpsPosition, namespace:vehiclebus GPS_Longitude=12.982076,GPS_Latitude=55.618748 1664787032948256000
45
+
46
+ Import:
47
+ influx write --org myorg -b my-bucket -p ns --format=lp -f signals.influx
48
+
49
+
50
+ """
51
+
52
+ # TODO: file is not properly opened using a context manager
53
+ if output is not None:
54
+ f = open(output, "w")
55
+
56
+ def exit_on_ctrlc(_sig: Any, _frame: Any) -> None:
57
+ if output is not None:
58
+ f.close()
59
+ sys.exit(0)
60
+
61
+ def per_frame_influx_line_protocol(x: Any) -> None:
62
+ signals = list(x)
63
+ if len(signals) == 0:
64
+ return
65
+ sig: str = signals[0]["name"].rpartition(".")[-1]
66
+ frame = signals[0]["name"].rsplit(".", 1)[0]
67
+ namespace = signals[0]["namespace"]
68
+ signals_str = ",".join(list(map(lambda s: f"{sig}={s['value']}", signals)))
69
+ influx_lp = f"{frame},namespace={namespace} {signals_str} {round(signals[0]['timestamp_us'] * 1000)}"
70
+ if output is not None:
71
+ f.write(f"{influx_lp}\n")
72
+ else:
73
+ # TODO: Use log instead of print for debug information?
74
+ print_generic_message(f"{influx_lp}")
75
+
76
+ os_signal.signal(os_signal.SIGINT, exit_on_ctrlc)
77
+
78
+ try:
79
+
80
+ def to_subscribable_signal(sig: str) -> SubscribableSignal:
81
+ arr = sig.split(":")
82
+ if len(arr) != 2:
83
+ print_hint(f"--signal must have format namespace:signal ({sig})")
84
+ sys.exit(1)
85
+
86
+ return SubscribableSignal(namespace=arr[0], name=arr[1])
87
+
88
+ signals_to_subscribe_to = list(map(to_subscribable_signal, signal))
89
+ broker = Broker(url, api_key)
90
+ broker.long_name_subscribe(signals_to_subscribe_to, per_frame_influx_line_protocol, on_change_only)
91
+ except grpc.RpcError as rpc_error:
92
+ print_grpc_error(rpc_error)
@@ -0,0 +1,119 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import List
5
+
6
+ import grpc
7
+ import typer
8
+ from rich.progress import Progress, SpinnerColumn, TextColumn
9
+
10
+ from remotivelabs.cli.broker.lib.broker import Broker
11
+ from remotivelabs.cli.typer import typer_utils
12
+ from remotivelabs.cli.utils.console import print_generic_error, print_grpc_error, print_success
13
+
14
+ app = typer_utils.create_typer(help=help)
15
+
16
+
17
+ @app.command()
18
+ def reload_configuration(
19
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
20
+ api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
21
+ ) -> None:
22
+ try:
23
+ broker = Broker(url, api_key)
24
+ broker.reload_config()
25
+ print_success("Configuration reloaded")
26
+ except grpc.RpcError as err:
27
+ print_grpc_error(err)
28
+
29
+
30
+ @app.command()
31
+ def delete(
32
+ path: List[str] = typer.Argument(..., help="Paths to files on broker to delete"),
33
+ exit_on_failure: bool = typer.Option(False, help="Exits if there was a problem deleting a file"),
34
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
35
+ api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
36
+ ) -> None:
37
+ """
38
+ Deletes the specified files from the broker
39
+ """
40
+ try:
41
+ broker = Broker(url, api_key)
42
+
43
+ if len(path) == 0:
44
+ print_generic_error("At least one path must be suppled")
45
+ raise typer.Exit(1)
46
+
47
+ broker.delete_files(path, exit_on_failure)
48
+ except grpc.RpcError as err:
49
+ print_grpc_error(err)
50
+
51
+
52
+ @app.command()
53
+ def download(
54
+ path: str = typer.Argument(..., help="Path to file on broker to download"),
55
+ output: str = typer.Option("", help="Optional output file name"),
56
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
57
+ api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
58
+ ) -> None:
59
+ """
60
+ Downloads a file from a broker
61
+ """
62
+ try:
63
+ with Progress(
64
+ SpinnerColumn(),
65
+ TextColumn("[progress.description]{task.description}"),
66
+ transient=True,
67
+ ) as progress:
68
+ progress.add_task(description=f"Downloading {path}...", total=None)
69
+ broker = Broker(url, api_key)
70
+ output_file = os.path.basename(path)
71
+ if output != "":
72
+ output_file = output
73
+ if os.path.exists(output_file):
74
+ print_generic_error(f"File already exist {output_file}, please use another output file name")
75
+ else:
76
+ broker.download(path, output_file)
77
+ print_success(f"{output_file} saved")
78
+ except grpc.RpcError as err:
79
+ print_grpc_error(err)
80
+
81
+
82
+ @app.command()
83
+ def upload(
84
+ path: str = typer.Argument(..., help="Path to local file to upload"),
85
+ output: str = typer.Option("", help="Optional output path on broker"),
86
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
87
+ api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
88
+ ) -> None:
89
+ """
90
+ Uploads a file to a broker - physical or in cloud.
91
+ """
92
+ try:
93
+ with Progress(
94
+ SpinnerColumn(),
95
+ TextColumn("[progress.description]{task.description}"),
96
+ transient=True,
97
+ ) as progress:
98
+ progress.add_task(description=f"Uploading {path}...", total=None)
99
+
100
+ if path == ".":
101
+ path = "./" ## Does not work otherwise
102
+
103
+ if not os.path.exists(path):
104
+ print_generic_error(f"File {path} does not exist")
105
+ raise typer.Exit(1)
106
+
107
+ broker = Broker(url, api_key)
108
+
109
+ if os.path.isdir(path):
110
+ broker.upload_folder(path)
111
+ print_success(f"{path} uploaded")
112
+ else:
113
+ output_file = os.path.basename(path)
114
+ if output != "":
115
+ output_file = output
116
+ broker.upload(path, output_file)
117
+ print_success(f"{path} uploaded")
118
+ except grpc.RpcError as err:
119
+ print_grpc_error(err)
@@ -0,0 +1,4 @@
1
+ # SPDX-FileCopyrightText: 2022-present remotiveLabs <support@remotivelabs.com>
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+ __version__ = "0.0.1"