remotivelabs-cli 0.0.25__py3-none-any.whl → 0.0.26__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.
- cli/broker/brokers.py +2 -2
- cli/broker/export.py +5 -5
- cli/broker/files.py +5 -5
- cli/broker/lib/broker.py +59 -49
- cli/broker/license_flows.py +11 -9
- cli/broker/licenses.py +2 -2
- cli/broker/playback.py +14 -34
- cli/broker/record.py +3 -3
- cli/broker/scripting.py +4 -4
- cli/broker/signals.py +11 -11
- cli/cloud/__init__.py +0 -1
- cli/cloud/auth.py +40 -35
- cli/cloud/auth_tokens.py +39 -36
- cli/cloud/brokers.py +24 -33
- cli/cloud/cloud_cli.py +9 -6
- cli/cloud/configs.py +19 -11
- cli/cloud/filestorage.py +63 -51
- cli/cloud/projects.py +10 -7
- cli/cloud/recordings.py +127 -108
- cli/cloud/recordings_playback.py +52 -39
- cli/cloud/rest_helper.py +247 -196
- cli/cloud/resumable_upload.py +9 -8
- cli/cloud/sample_recordings.py +5 -5
- cli/cloud/service_account_tokens.py +18 -16
- cli/cloud/service_accounts.py +9 -9
- cli/connect/__init__.py +0 -1
- cli/connect/connect.py +7 -6
- cli/connect/protopie/protopie.py +32 -16
- cli/errors.py +6 -5
- cli/remotive.py +13 -9
- cli/requirements.txt +4 -1
- cli/settings.py +9 -9
- cli/tools/__init__.py +0 -1
- cli/tools/can/__init__.py +0 -1
- cli/tools/can/can.py +8 -8
- cli/tools/tools.py +2 -2
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/METADATA +5 -3
- remotivelabs_cli-0.0.26.dist-info/RECORD +44 -0
- remotivelabs_cli-0.0.25.dist-info/RECORD +0 -44
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.0.25.dist-info → remotivelabs_cli-0.0.26.dist-info}/entry_points.txt +0 -0
cli/broker/brokers.py
CHANGED
@@ -19,7 +19,7 @@ app = typer.Typer(rich_markup_mode="rich")
|
|
19
19
|
@app.callback()
|
20
20
|
def main(
|
21
21
|
url: str = typer.Option(None, is_eager=False, help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
22
|
-
):
|
22
|
+
) -> None:
|
23
23
|
# This can be used to override the --url per command, lets see if this is a better approach
|
24
24
|
if url is not None:
|
25
25
|
os.environ["REMOTIVE_BROKER_URL"] = url
|
@@ -28,7 +28,7 @@ def main(
|
|
28
28
|
|
29
29
|
|
30
30
|
@app.command(help="Discover brokers on this network")
|
31
|
-
def discover():
|
31
|
+
def discover() -> None:
|
32
32
|
# print("Not implemented")
|
33
33
|
|
34
34
|
zeroconf = Zeroconf(ip_version=IPVersion.V4Only)
|
cli/broker/export.py
CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import os
|
4
4
|
import signal as os_signal
|
5
|
-
from typing import List
|
5
|
+
from typing import Any, List
|
6
6
|
|
7
7
|
import grpc
|
8
8
|
import typer
|
@@ -28,7 +28,7 @@ def influxdb(
|
|
28
28
|
# namespace: str = typer.Option(..., help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
29
29
|
on_change_only: bool = typer.Option(default=False, help="Only get signal if value is changed"),
|
30
30
|
output: str = typer.Option(None, help="Write results to file, defaults to stdout"),
|
31
|
-
):
|
31
|
+
) -> None:
|
32
32
|
"""
|
33
33
|
Exports subscribed signals to InfluxDB line-protocol, really useful to dump some signals into
|
34
34
|
influxdb for offline analysis and insights.
|
@@ -53,12 +53,12 @@ def influxdb(
|
|
53
53
|
if output is not None:
|
54
54
|
f = open(output, "w")
|
55
55
|
|
56
|
-
def exit_on_ctrlc(sig, frame):
|
56
|
+
def exit_on_ctrlc(sig: Any, frame: Any) -> None:
|
57
57
|
if output is not None:
|
58
58
|
f.close()
|
59
59
|
os._exit(0)
|
60
60
|
|
61
|
-
def per_frame_influx_line_protocol(x):
|
61
|
+
def per_frame_influx_line_protocol(x: Any) -> None:
|
62
62
|
signals = list(x)
|
63
63
|
if len(signals) == 0:
|
64
64
|
return
|
@@ -107,7 +107,7 @@ def influxdb(
|
|
107
107
|
# signals2 = list(map( lambda s: s['signal'], broker.list_signal_names2(namespace)))
|
108
108
|
try:
|
109
109
|
|
110
|
-
def to_subscribable_signal(sig: str):
|
110
|
+
def to_subscribable_signal(sig: str) -> SubscribableSignal:
|
111
111
|
arr = sig.split(":")
|
112
112
|
if len(arr) != 2:
|
113
113
|
ErrorPrinter.print_hint(f"--signal must have format namespace:signal ({sig})")
|
cli/broker/files.py
CHANGED
@@ -11,14 +11,14 @@ from cli.errors import ErrorPrinter
|
|
11
11
|
|
12
12
|
from .lib.broker import Broker
|
13
13
|
|
14
|
-
app = typer.Typer(help=help)
|
14
|
+
app = typer.Typer(help=help) # type: ignore
|
15
15
|
|
16
16
|
|
17
17
|
@app.command()
|
18
18
|
def reload_configuration(
|
19
19
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
20
20
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
21
|
-
):
|
21
|
+
) -> None:
|
22
22
|
try:
|
23
23
|
broker = Broker(url, api_key)
|
24
24
|
broker.reload_config()
|
@@ -33,7 +33,7 @@ def delete(
|
|
33
33
|
exit_on_failure: bool = typer.Option(False, help="Exits if there was a problem deleting a file"),
|
34
34
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
35
35
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
36
|
-
):
|
36
|
+
) -> None:
|
37
37
|
"""
|
38
38
|
Deletes the specified files from the broker
|
39
39
|
"""
|
@@ -55,7 +55,7 @@ def download(
|
|
55
55
|
output: str = typer.Option("", help="Optional output file name"),
|
56
56
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
57
57
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
58
|
-
):
|
58
|
+
) -> None:
|
59
59
|
"""
|
60
60
|
Downloads a file from a broker
|
61
61
|
"""
|
@@ -85,7 +85,7 @@ def upload(
|
|
85
85
|
output: str = typer.Option("", help="Optional output path on broker"),
|
86
86
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
87
87
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
88
|
-
):
|
88
|
+
) -> None:
|
89
89
|
"""
|
90
90
|
Uploads a file to a broker - physical or in cloud.
|
91
91
|
"""
|
cli/broker/lib/broker.py
CHANGED
@@ -8,11 +8,10 @@ import queue
|
|
8
8
|
import signal as os_signal
|
9
9
|
import tempfile
|
10
10
|
import time
|
11
|
-
import typing
|
12
11
|
import zipfile
|
13
12
|
from dataclasses import dataclass
|
14
13
|
from threading import Thread
|
15
|
-
from typing import Callable, List, Sequence, Union
|
14
|
+
from typing import Any, Callable, Dict, Iterable, List, Sequence, Union
|
16
15
|
|
17
16
|
import grpc
|
18
17
|
import remotivelabs.broker.generated.sync.traffic_api_pb2 as traffic_api
|
@@ -42,10 +41,10 @@ class LicenseInfo:
|
|
42
41
|
|
43
42
|
|
44
43
|
class Broker:
|
45
|
-
def __init__(self, url: str, api_key: Union[str, None] = None):
|
44
|
+
def __init__(self, url: str, api_key: Union[str, None] = None) -> None:
|
46
45
|
self.url = url
|
47
46
|
self.api_key = api_key
|
48
|
-
self.q = queue.Queue()
|
47
|
+
self.q: queue.Queue[Any] = queue.Queue()
|
49
48
|
"""Main function, checking arguments passed to script, setting up stubs, configuration and starting Threads."""
|
50
49
|
# Setting up stubs and configuration
|
51
50
|
|
@@ -70,7 +69,7 @@ class Broker:
|
|
70
69
|
self.signal_creator = br.SignalCreator(self.system_stub)
|
71
70
|
|
72
71
|
@staticmethod
|
73
|
-
def __check_playbackmode_result(status):
|
72
|
+
def __check_playbackmode_result(status: br.traffic_api_pb2.PlaybackInfos) -> None:
|
74
73
|
err_cnt = 0
|
75
74
|
for mode in status.playbackInfo:
|
76
75
|
if mode.playbackMode.errorMessage:
|
@@ -79,8 +78,8 @@ class Broker:
|
|
79
78
|
if err_cnt > 0:
|
80
79
|
raise typer.Exit(1)
|
81
80
|
|
82
|
-
def seek(self, recording_and_namespace: List, offset: int, silent: bool = True):
|
83
|
-
def to_playback(rec):
|
81
|
+
def seek(self, recording_and_namespace: List[Any], offset: int, silent: bool = True) -> br.traffic_api_pb2.PlaybackInfos:
|
82
|
+
def to_playback(rec: Any) -> Dict[str, Any]:
|
84
83
|
return {"namespace": rec["namespace"], "path": rec["recording"], "mode": br.traffic_api_pb2.Mode.SEEK, "offsettime": offset}
|
85
84
|
|
86
85
|
playback_list = map(to_playback, recording_and_namespace)
|
@@ -91,8 +90,8 @@ class Broker:
|
|
91
90
|
self.__check_playbackmode_result(status)
|
92
91
|
return status
|
93
92
|
|
94
|
-
def play(self, recording_and_namespace: List, silent: bool = False):
|
95
|
-
def to_playback(rec):
|
93
|
+
def play(self, recording_and_namespace: List[Any], silent: bool = False) -> br.traffic_api_pb2.PlaybackInfos:
|
94
|
+
def to_playback(rec: Any) -> Dict[str, Any]:
|
96
95
|
return {
|
97
96
|
"namespace": rec["namespace"],
|
98
97
|
"path": rec["recording"],
|
@@ -109,8 +108,8 @@ class Broker:
|
|
109
108
|
self.__check_playbackmode_result(status)
|
110
109
|
return status
|
111
110
|
|
112
|
-
def stop_play(self, recording_and_namespace: List, silent: bool = False):
|
113
|
-
def to_playback(rec):
|
111
|
+
def stop_play(self, recording_and_namespace: List[Any], silent: bool = False) -> br.traffic_api_pb2.PlaybackInfos:
|
112
|
+
def to_playback(rec: Any) -> Dict[str, Any]:
|
114
113
|
return {
|
115
114
|
"namespace": rec["namespace"],
|
116
115
|
"path": rec["recording"],
|
@@ -126,8 +125,8 @@ class Broker:
|
|
126
125
|
self.__check_playbackmode_result(status)
|
127
126
|
return status
|
128
127
|
|
129
|
-
def pause_play(self, recording_and_namespace: List, silent: bool = False):
|
130
|
-
def to_playback(rec):
|
128
|
+
def pause_play(self, recording_and_namespace: List[Any], silent: bool = False) -> br.traffic_api_pb2.PlaybackInfos:
|
129
|
+
def to_playback(rec: Any) -> Dict[str, Any]:
|
131
130
|
return {
|
132
131
|
"namespace": rec["namespace"],
|
133
132
|
"path": rec["recording"],
|
@@ -143,8 +142,8 @@ class Broker:
|
|
143
142
|
self.__check_playbackmode_result(status)
|
144
143
|
return status
|
145
144
|
|
146
|
-
def record_multiple(self, namespaces: List[str], path: str):
|
147
|
-
def to_playback(namespace):
|
145
|
+
def record_multiple(self, namespaces: List[str], path: str) -> br.traffic_api_pb2.PlaybackInfos:
|
146
|
+
def to_playback(namespace: str) -> Dict[str, Any]:
|
148
147
|
return {
|
149
148
|
"namespace": namespace,
|
150
149
|
"path": path + "_" + namespace,
|
@@ -159,7 +158,7 @@ class Broker:
|
|
159
158
|
self.__check_playbackmode_result(status)
|
160
159
|
return status
|
161
160
|
|
162
|
-
def record(self, namespace: str, path: str):
|
161
|
+
def record(self, namespace: str, path: str) -> br.traffic_api_pb2.PlaybackInfos:
|
163
162
|
playback_list = [
|
164
163
|
{
|
165
164
|
"namespace": namespace,
|
@@ -174,7 +173,7 @@ class Broker:
|
|
174
173
|
self.__check_playbackmode_result(status)
|
175
174
|
return status
|
176
175
|
|
177
|
-
def stop(self, namespace: str, path: str, silent: bool = False):
|
176
|
+
def stop(self, namespace: str, path: str, silent: bool = False) -> br.traffic_api_pb2.PlaybackInfos:
|
178
177
|
playback_list = [
|
179
178
|
{
|
180
179
|
"namespace": namespace,
|
@@ -190,20 +189,22 @@ class Broker:
|
|
190
189
|
self.__check_playbackmode_result(status)
|
191
190
|
return status
|
192
191
|
|
193
|
-
def listen_on_playback(self, repeat: bool, recording_and_namespace: List, callback: Callable[[int, int, str], None]):
|
192
|
+
def listen_on_playback(self, repeat: bool, recording_and_namespace: List[Any], callback: Callable[[int, int, str], None]) -> None:
|
194
193
|
# include recording_and_namespace if we want to loop the recording
|
195
194
|
# This can probably be improved
|
196
|
-
def get_mode(mode: int):
|
195
|
+
def get_mode(mode: int) -> str:
|
197
196
|
if mode == 0:
|
198
197
|
return "playing"
|
199
198
|
if mode == 1:
|
200
199
|
return "paused"
|
201
200
|
if mode == 2:
|
202
201
|
return "stopped"
|
202
|
+
raise ValueError("Unknown Mode")
|
203
203
|
|
204
204
|
sub = self.traffic_stub.PlayTrafficStatus(br.common_pb2.Empty())
|
205
205
|
for playback_state in sub:
|
206
|
-
p = typing.cast(br.traffic_api_pb2.PlaybackInfos, playback_state)
|
206
|
+
# p = typing.cast(br.traffic_api_pb2.PlaybackInfos, playback_state) # REDUNDANT CAST
|
207
|
+
p = playback_state
|
207
208
|
offset_length = int(p.playbackInfo[0].playbackMode.offsetTime / 1000000)
|
208
209
|
start_time = p.playbackInfo[0].playbackMode.startTime
|
209
210
|
end_time = p.playbackInfo[0].playbackMode.endTime
|
@@ -218,7 +219,7 @@ class Broker:
|
|
218
219
|
self.play(recording_and_namespace)
|
219
220
|
callback(offset_length, total_length, get_mode(mode))
|
220
221
|
|
221
|
-
def pause(self, namespace: str, path: str, silent: bool = False):
|
222
|
+
def pause(self, namespace: str, path: str, silent: bool = False) -> br.traffic_api_pb2.PlaybackInfos:
|
222
223
|
playback_list = [
|
223
224
|
{
|
224
225
|
"namespace": namespace,
|
@@ -234,8 +235,8 @@ class Broker:
|
|
234
235
|
self.__check_playbackmode_result(status)
|
235
236
|
return status
|
236
237
|
|
237
|
-
def stop_multiple(self, namespaces: List[str], path: str):
|
238
|
-
def to_playback(namespace):
|
238
|
+
def stop_multiple(self, namespaces: List[str], path: str) -> br.traffic_api_pb2.PlaybackInfos:
|
239
|
+
def to_playback(namespace: str) -> Dict[str, Any]:
|
239
240
|
return {
|
240
241
|
"namespace": namespace,
|
241
242
|
"path": path + "_" + namespace,
|
@@ -250,17 +251,17 @@ class Broker:
|
|
250
251
|
self.__check_playbackmode_result(status)
|
251
252
|
return status
|
252
253
|
|
253
|
-
def diagnose_stop(self, namespace: List[str]):
|
254
|
+
def diagnose_stop(self, namespace: List[str]) -> None:
|
254
255
|
recording_name = "diagnose__"
|
255
256
|
self.stop_multiple(namespace, recording_name)
|
256
257
|
|
257
|
-
def diagnose(self, namespace: List[str], wait_for_traffic: bool = False):
|
258
|
+
def diagnose(self, namespace: List[str], wait_for_traffic: bool = False) -> None:
|
258
259
|
recording_name = "diagnose__"
|
259
260
|
|
260
261
|
keep_running = True
|
261
262
|
keep_running_during_recording = True
|
262
263
|
|
263
|
-
def exit_on_ctrlc(sig, frame):
|
264
|
+
def exit_on_ctrlc(sig: Any, frame: Any) -> None:
|
264
265
|
nonlocal keep_running
|
265
266
|
keep_running = False
|
266
267
|
nonlocal keep_running_during_recording
|
@@ -299,7 +300,7 @@ class Broker:
|
|
299
300
|
if not wait_for_traffic or (not keep_running and not keep_running_during_recording):
|
300
301
|
print(f'Namespace {r["namespace"]} did not receive any traffic')
|
301
302
|
|
302
|
-
def upload(self, file: str, dest: str):
|
303
|
+
def upload(self, file: str, dest: str) -> None:
|
303
304
|
try:
|
304
305
|
br.helper.upload_file(system_stub=self.system_stub, path=file, dest_path=dest)
|
305
306
|
except grpc.RpcError as rpc_error:
|
@@ -321,14 +322,14 @@ class Broker:
|
|
321
322
|
|
322
323
|
# rpc BatchDeleteFiles (FileDescriptions) returns (Empty) {}
|
323
324
|
|
324
|
-
def upload_folder(self, folder: str):
|
325
|
+
def upload_folder(self, folder: str) -> None:
|
325
326
|
try:
|
326
327
|
br.helper.upload_folder(system_stub=self.system_stub, folder=folder)
|
327
328
|
except grpc.RpcError as rpc_error:
|
328
329
|
print(f"Failed to upload file - {rpc_error.details()} ({rpc_error.code()})")
|
329
330
|
raise typer.Exit(1)
|
330
331
|
|
331
|
-
def download(self, file: str, dest: str, delegate_err: bool = False):
|
332
|
+
def download(self, file: str, dest: str, delegate_err: bool = False) -> None:
|
332
333
|
try:
|
333
334
|
br_helper.download_file(system_stub=self.system_stub, path=file, dest_path=dest)
|
334
335
|
except grpc.RpcError as rpc_error:
|
@@ -339,7 +340,7 @@ class Broker:
|
|
339
340
|
os.remove(dest)
|
340
341
|
raise typer.Exit(1)
|
341
342
|
|
342
|
-
def reload_config(self):
|
343
|
+
def reload_config(self) -> None:
|
343
344
|
try:
|
344
345
|
request = br.common_pb2.Empty()
|
345
346
|
response = self.system_stub.ReloadConfiguration(request, timeout=60000)
|
@@ -351,7 +352,7 @@ class Broker:
|
|
351
352
|
print(f"Failed to reload configuration - {rpc_error.details()} ({rpc_error.code()})")
|
352
353
|
raise typer.Exit(1)
|
353
354
|
|
354
|
-
def list_namespaces(self):
|
355
|
+
def list_namespaces(self) -> List[str]:
|
355
356
|
# Lists available signals
|
356
357
|
configuration = self.system_stub.GetConfiguration(br.common_pb2.Empty())
|
357
358
|
namespaces = []
|
@@ -359,7 +360,7 @@ class Broker:
|
|
359
360
|
namespaces.append(network_info.namespace.name)
|
360
361
|
return namespaces
|
361
362
|
|
362
|
-
def list_signal_names(self):
|
363
|
+
def list_signal_names(self) -> List[Dict[str, str]]:
|
363
364
|
# Lists available signals
|
364
365
|
configuration = self.system_stub.GetConfiguration(br.common_pb2.Empty())
|
365
366
|
|
@@ -378,7 +379,7 @@ class Broker:
|
|
378
379
|
script: bytes,
|
379
380
|
on_frame: Callable[[Sequence[br.network_api_pb2.Signal]], None],
|
380
381
|
changed_values_only: bool = False,
|
381
|
-
) ->
|
382
|
+
) -> Any:
|
382
383
|
client_id = br.common_pb2.ClientId(id="cli")
|
383
384
|
# sync = queue.Queue()
|
384
385
|
thread = Thread(
|
@@ -406,11 +407,11 @@ class Broker:
|
|
406
407
|
# over namespaces
|
407
408
|
# Begin
|
408
409
|
|
409
|
-
def verify_namespace(available_signal):
|
410
|
-
return list(filter(lambda namespace: available_signal["namespace"] == namespace, subscribed_namespaces))
|
410
|
+
def verify_namespace(available_signal: List[Dict[str, str]]) -> List[str]:
|
411
|
+
return list(filter(lambda namespace: available_signal["namespace"] == namespace, subscribed_namespaces)) # type: ignore
|
411
412
|
|
412
|
-
def find_subscribed_signal(available_signal):
|
413
|
-
return list(filter(lambda s: available_signal["signal"] == s, subscribed_signals))
|
413
|
+
def find_subscribed_signal(available_signal: List[Dict[str, str]]) -> List[str]:
|
414
|
+
return list(filter(lambda s: available_signal["signal"] == s, subscribed_signals)) # type: ignore
|
414
415
|
|
415
416
|
existing_signals = self.list_signal_names()
|
416
417
|
existing_ns = set(map(lambda s: s["namespace"], existing_signals))
|
@@ -424,8 +425,8 @@ class Broker:
|
|
424
425
|
)
|
425
426
|
exit(1)
|
426
427
|
|
427
|
-
available_signals = list(filter(verify_namespace, existing_signals))
|
428
|
-
signals_to_subscribe_to = list(filter(find_subscribed_signal, available_signals))
|
428
|
+
available_signals = list(filter(verify_namespace, existing_signals)) # type: ignore
|
429
|
+
signals_to_subscribe_to = list(filter(find_subscribed_signal, available_signals)) # type: ignore
|
429
430
|
|
430
431
|
# Check if subscription is done on signal that is not in any of these namespaces
|
431
432
|
signals_subscribed_to_but_does_not_exist = set(subscribed_signals) - set(map(lambda s: s["signal"], signals_to_subscribe_to))
|
@@ -436,7 +437,9 @@ class Broker:
|
|
436
437
|
|
437
438
|
return list(map(lambda s: SubscribableSignal(s["signal"], s["namespace"]), signals_to_subscribe_to))
|
438
439
|
|
439
|
-
def long_name_subscribe(
|
440
|
+
def long_name_subscribe(
|
441
|
+
self, signals_to_subscribe_to: List[SubscribableSignal], on_frame: Callable[..., Any], changed_values_only: bool = True
|
442
|
+
) -> Any:
|
440
443
|
client_id = br.common_pb2.ClientId(id="cli")
|
441
444
|
|
442
445
|
# TODO - This can be improved moving forward and we also need to move the validation into api
|
@@ -444,7 +447,7 @@ class Broker:
|
|
444
447
|
list(map(lambda s: s.namespace, signals_to_subscribe_to)), (list(map(lambda s: s.name, signals_to_subscribe_to)))
|
445
448
|
)
|
446
449
|
|
447
|
-
def to_protobuf_signal(s: SubscribableSignal):
|
450
|
+
def to_protobuf_signal(s: SubscribableSignal) -> br.common_pb2.SignalId:
|
448
451
|
return self.signal_creator.signal(s.name, s.namespace)
|
449
452
|
|
450
453
|
signals_to_subscribe_on = list(map(to_protobuf_signal, signals_to_subscribe_to))
|
@@ -464,14 +467,20 @@ class Broker:
|
|
464
467
|
ecu, subscription = self.q.get()
|
465
468
|
return subscription
|
466
469
|
|
467
|
-
def subscribe(
|
470
|
+
def subscribe(
|
471
|
+
self,
|
472
|
+
subscribed_signals: list[str],
|
473
|
+
subscribed_namespaces: list[str],
|
474
|
+
on_frame: Callable[..., Any],
|
475
|
+
changed_values_only: bool = True,
|
476
|
+
) -> Any:
|
468
477
|
client_id = br.common_pb2.ClientId(id="cli")
|
469
478
|
|
470
479
|
signals_to_subscribe_to: List[SubscribableSignal] = self.validate_and_get_subscribed_signals(
|
471
480
|
subscribed_namespaces, subscribed_signals
|
472
481
|
)
|
473
482
|
|
474
|
-
def to_protobuf_signal(s: SubscribableSignal):
|
483
|
+
def to_protobuf_signal(s: SubscribableSignal) -> br.common_pb2.SignalId:
|
475
484
|
return self.signal_creator.signal(s.name, s.namespace)
|
476
485
|
|
477
486
|
signals_to_subscribe_on = list(map(to_protobuf_signal, signals_to_subscribe_to))
|
@@ -491,7 +500,7 @@ class Broker:
|
|
491
500
|
ecu, subscription = self.q.get()
|
492
501
|
return subscription
|
493
502
|
|
494
|
-
def __each_signal(self, signals, callback):
|
503
|
+
def __each_signal(self, signals: Iterable[br.network_api_pb2.Signal], callback: Callable[..., Any]) -> None:
|
495
504
|
callback(
|
496
505
|
map(
|
497
506
|
lambda s: {"timestamp_us": s.timestamp, "namespace": s.id.namespace.name, "name": s.id.name, "value": self.__get_value(s)},
|
@@ -500,7 +509,7 @@ class Broker:
|
|
500
509
|
)
|
501
510
|
|
502
511
|
@staticmethod
|
503
|
-
def __get_value(signal):
|
512
|
+
def __get_value(signal: br.network_api_pb2.Signal) -> Any:
|
504
513
|
if signal.raw != b"":
|
505
514
|
return "0x" + binascii.hexlify(signal.raw).decode("ascii")
|
506
515
|
if signal.HasField("integer"):
|
@@ -512,7 +521,7 @@ class Broker:
|
|
512
521
|
return "empty"
|
513
522
|
|
514
523
|
@staticmethod
|
515
|
-
def __create_playback_config(item):
|
524
|
+
def __create_playback_config(item: Dict[str, Any]) -> br.traffic_api_pb2.PlaybackInfo:
|
516
525
|
"""Creating configuration for playback
|
517
526
|
|
518
527
|
Parameters
|
@@ -527,9 +536,10 @@ class Broker:
|
|
527
536
|
|
528
537
|
"""
|
529
538
|
|
530
|
-
def get_offset_time():
|
539
|
+
def get_offset_time() -> int:
|
531
540
|
if "offsettime" in item:
|
532
|
-
return item["offsettime"]
|
541
|
+
return int(item["offsettime"])
|
542
|
+
return 0
|
533
543
|
|
534
544
|
playback_config = br.traffic_api_pb2.PlaybackConfig(
|
535
545
|
fileDescription=br.system_api_pb2.FileDescription(path=item["path"]),
|
@@ -549,7 +559,7 @@ class Broker:
|
|
549
559
|
machine_id=license_info.requestMachineId.decode("utf-8"),
|
550
560
|
)
|
551
561
|
|
552
|
-
def apply_license(self, license_data_b64: bytes):
|
562
|
+
def apply_license(self, license_data_b64: bytes) -> LicenseInfo:
|
553
563
|
license = br.system_api_pb2.License()
|
554
564
|
license.data = license_data_b64
|
555
565
|
license.termsAgreement = True
|
cli/broker/license_flows.py
CHANGED
@@ -10,8 +10,8 @@ import typer
|
|
10
10
|
from rich.console import Console
|
11
11
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
12
12
|
|
13
|
-
import cli.cloud.rest_helper as rest
|
14
13
|
from cli.broker.lib.broker import Broker, LicenseInfo
|
14
|
+
from cli.cloud.rest_helper import RestHelper as Rest
|
15
15
|
|
16
16
|
console = Console(stderr=True)
|
17
17
|
|
@@ -25,7 +25,7 @@ class LicenseFlow:
|
|
25
25
|
)
|
26
26
|
return b.get_license()
|
27
27
|
|
28
|
-
def describe_with_hotspot(self, url: Union[str, None] = "http://192.168.4.1:50051"):
|
28
|
+
def describe_with_hotspot(self, url: Union[str, None] = "http://192.168.4.1:50051") -> LicenseInfo:
|
29
29
|
if url is None:
|
30
30
|
url = "http://192.168.4.1:50051"
|
31
31
|
|
@@ -34,7 +34,7 @@ class LicenseFlow:
|
|
34
34
|
)
|
35
35
|
return b.get_license()
|
36
36
|
|
37
|
-
def request_with_url_with_internet(self, url: str):
|
37
|
+
def request_with_url_with_internet(self, url: str) -> None:
|
38
38
|
console.print("This requires internet connection from your computer during the entire licensing process")
|
39
39
|
|
40
40
|
email = LicenseFlow.__try_authenticate_and_get_email_from_cloud()
|
@@ -68,7 +68,7 @@ class LicenseFlow:
|
|
68
68
|
if new_license.valid:
|
69
69
|
console.print(f":thumbsup: Successfully applied license, it remains valid until {new_license.expires}")
|
70
70
|
|
71
|
-
def request_with_hotspot(self, url: Union[str, None] = "http://192.168.4.1:50051"):
|
71
|
+
def request_with_hotspot(self, url: Union[str, None] = "http://192.168.4.1:50051") -> None:
|
72
72
|
"""
|
73
73
|
This flow expects changes between networks and tries to guide the user accordingly
|
74
74
|
:param url: If None it will use the default hotspot IP
|
@@ -137,12 +137,14 @@ class LicenseFlow:
|
|
137
137
|
with use_progress("Fetching user info from cloud... Make sure you are connected to internet"):
|
138
138
|
while True:
|
139
139
|
try:
|
140
|
-
r =
|
140
|
+
r = Rest.handle_get("/api/whoami", return_response=True, use_progress_indicator=False, timeout=5)
|
141
141
|
break
|
142
142
|
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
|
143
143
|
time.sleep(1)
|
144
144
|
console.print(":white_check_mark: Fetching user info from cloud... Make sure you are connected to internet")
|
145
|
-
|
145
|
+
if r is None:
|
146
|
+
return ""
|
147
|
+
return str(r.json()["email"])
|
146
148
|
|
147
149
|
@staticmethod
|
148
150
|
def __try_request_license(
|
@@ -153,15 +155,15 @@ class LicenseFlow:
|
|
153
155
|
with use_progress(progress_label):
|
154
156
|
while True:
|
155
157
|
try:
|
156
|
-
license_data_b64 =
|
158
|
+
license_data_b64 = Rest.request_license(email, json.loads(existing_license.machine_id)).encode()
|
157
159
|
break
|
158
160
|
except (requests.exceptions.ConnectionError, requests.exceptions.Timeout):
|
159
161
|
time.sleep(1)
|
160
162
|
console.print(f":white_check_mark: {progress_label}")
|
161
|
-
return license_data_b64
|
163
|
+
return bytes(license_data_b64)
|
162
164
|
|
163
165
|
|
164
|
-
def use_progress(label: str):
|
166
|
+
def use_progress(label: str) -> Progress:
|
165
167
|
p = Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True, expand=False)
|
166
168
|
p.add_task(label, total=1)
|
167
169
|
return p
|
cli/broker/licenses.py
CHANGED
@@ -35,7 +35,7 @@ class Connection(str, Enum):
|
|
35
35
|
def describe(
|
36
36
|
connect: Connection = typer.Option("url", case_sensitive=False, help="How to connect to broker"),
|
37
37
|
url: str = typer.Option("http://localhost:50051", is_eager=False, help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
38
|
-
):
|
38
|
+
) -> None:
|
39
39
|
"""
|
40
40
|
Show licence information
|
41
41
|
|
@@ -70,7 +70,7 @@ def request(
|
|
70
70
|
help="Broker url, this is mandatory when connect type is 'url'",
|
71
71
|
envvar="REMOTIVE_BROKER_URL",
|
72
72
|
),
|
73
|
-
):
|
73
|
+
) -> None:
|
74
74
|
"""
|
75
75
|
Requests and applies a new or existing License to a broker, Note that internet access is required on your
|
76
76
|
computer
|
cli/broker/playback.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from typing import List
|
3
|
+
from typing import Dict, List
|
4
4
|
|
5
5
|
import grpc
|
6
6
|
import typer
|
@@ -9,7 +9,15 @@ from cli.errors import ErrorPrinter
|
|
9
9
|
|
10
10
|
from .lib.broker import Broker
|
11
11
|
|
12
|
-
app = typer.Typer(help=help)
|
12
|
+
app = typer.Typer(help=help) # type: ignore
|
13
|
+
|
14
|
+
|
15
|
+
def recording_and_namespace(recording: str) -> Dict[str, str]:
|
16
|
+
splitted = recording.split("::")
|
17
|
+
if len(splitted) != 2:
|
18
|
+
print("Invalid --recording option, expected file_name::namespace")
|
19
|
+
raise typer.Exit(1)
|
20
|
+
return {"recording": splitted[0], "namespace": splitted[1]}
|
13
21
|
|
14
22
|
|
15
23
|
@app.command()
|
@@ -17,7 +25,7 @@ def play(
|
|
17
25
|
recording: List[str] = typer.Option(..., help="Which recording and which namespace to play"),
|
18
26
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
19
27
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
20
|
-
):
|
28
|
+
) -> None:
|
21
29
|
"""
|
22
30
|
Play recording files on broker.
|
23
31
|
|
@@ -26,13 +34,6 @@ def play(
|
|
26
34
|
remotive broker playback play --recording myrecording_can0::can0 --recording myrecording_can1::can1
|
27
35
|
"""
|
28
36
|
|
29
|
-
def recording_and_namespace(recording: str):
|
30
|
-
splitted = recording.split("::")
|
31
|
-
if len(splitted) != 2:
|
32
|
-
print("Invalid --recording option, expected file_name::namespace")
|
33
|
-
raise typer.Exit(1)
|
34
|
-
return {"recording": splitted[0], "namespace": splitted[1]}
|
35
|
-
|
36
37
|
rec = list(map(recording_and_namespace, recording))
|
37
38
|
try:
|
38
39
|
broker = Broker(url, api_key)
|
@@ -48,7 +49,7 @@ def stop(
|
|
48
49
|
recording: List[str] = typer.Option(..., help="Which recording and which namespace to stop"),
|
49
50
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
50
51
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
51
|
-
):
|
52
|
+
) -> None:
|
52
53
|
"""
|
53
54
|
Stop recordings that are beeing played on brokers are done with the same syntax as when you start them.
|
54
55
|
|
@@ -57,13 +58,6 @@ def stop(
|
|
57
58
|
remotive broker playback stop --recording myrecording_can0::can0 --recording myrecording_can1::can1
|
58
59
|
"""
|
59
60
|
|
60
|
-
def recording_and_namespace(recording: str):
|
61
|
-
splitted = recording.split("::")
|
62
|
-
if len(splitted) != 2:
|
63
|
-
print("Invalid --recording option, expected file_name::namespace")
|
64
|
-
raise typer.Exit(1)
|
65
|
-
return {"recording": splitted[0], "namespace": splitted[1]}
|
66
|
-
|
67
61
|
rec = list(map(recording_and_namespace, recording))
|
68
62
|
|
69
63
|
try:
|
@@ -79,7 +73,7 @@ def pause(
|
|
79
73
|
recording: List[str] = typer.Option(..., help="Which recording and which namespace to stop"),
|
80
74
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
81
75
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
82
|
-
):
|
76
|
+
) -> None:
|
83
77
|
"""
|
84
78
|
Pause recordings that are beeing played on brokers are done with the same syntax as when you start them.
|
85
79
|
|
@@ -88,13 +82,6 @@ def pause(
|
|
88
82
|
remotive broker playback pause --recording myrecording_can0::can0 --recording myrecording_can1::can1
|
89
83
|
"""
|
90
84
|
|
91
|
-
def recording_and_namespace(recording: str):
|
92
|
-
splitted = recording.split("::")
|
93
|
-
if len(splitted) != 2:
|
94
|
-
print("Invalid --recording option, expected file_name::namespace")
|
95
|
-
raise typer.Exit(1)
|
96
|
-
return {"recording": splitted[0], "namespace": splitted[1]}
|
97
|
-
|
98
85
|
rec = list(map(recording_and_namespace, recording))
|
99
86
|
try:
|
100
87
|
broker = Broker(url, api_key)
|
@@ -110,7 +97,7 @@ def seek(
|
|
110
97
|
seconds: float = typer.Option(..., help="Target offset in seconds"),
|
111
98
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
112
99
|
api_key: str = typer.Option("offline", help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
|
113
|
-
):
|
100
|
+
) -> None:
|
114
101
|
"""
|
115
102
|
Seeks to a position in seconds into the recording
|
116
103
|
|
@@ -121,13 +108,6 @@ def seek(
|
|
121
108
|
|
122
109
|
broker = Broker(url, api_key)
|
123
110
|
|
124
|
-
def recording_and_namespace(recording: str):
|
125
|
-
splitted = recording.split("::")
|
126
|
-
if len(splitted) != 2:
|
127
|
-
print("Invalid --recording option, expected file_name::namespace")
|
128
|
-
raise typer.Exit(1)
|
129
|
-
return {"recording": splitted[0], "namespace": splitted[1]}
|
130
|
-
|
131
111
|
rec = list(map(recording_and_namespace, recording))
|
132
112
|
|
133
113
|
try:
|