remotivelabs-cli 0.0.14__py3-none-any.whl → 0.0.16__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/export.py +24 -12
- cli/broker/lib/broker.py +17 -6
- cli/cloud/recordings_playback.py +56 -22
- {remotivelabs_cli-0.0.14.dist-info → remotivelabs_cli-0.0.16.dist-info}/METADATA +1 -1
- {remotivelabs_cli-0.0.14.dist-info → remotivelabs_cli-0.0.16.dist-info}/RECORD +8 -8
- {remotivelabs_cli-0.0.14.dist-info → remotivelabs_cli-0.0.16.dist-info}/LICENSE +0 -0
- {remotivelabs_cli-0.0.14.dist-info → remotivelabs_cli-0.0.16.dist-info}/WHEEL +0 -0
- {remotivelabs_cli-0.0.14.dist-info → remotivelabs_cli-0.0.16.dist-info}/entry_points.txt +0 -0
cli/broker/export.py
CHANGED
@@ -9,7 +9,7 @@ import typer
|
|
9
9
|
|
10
10
|
from cli.errors import ErrorPrinter
|
11
11
|
|
12
|
-
from .lib.broker import Broker
|
12
|
+
from .lib.broker import Broker, SubscribableSignal
|
13
13
|
|
14
14
|
app = typer.Typer(
|
15
15
|
rich_markup_mode="rich",
|
@@ -24,8 +24,8 @@ but more formats will come soon
|
|
24
24
|
def influxdb(
|
25
25
|
url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
|
26
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"),
|
28
|
-
namespace: str = typer.Option(..., help="Cloud Broker API-KEY or access token", 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
|
+
# 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
|
):
|
@@ -37,12 +37,12 @@ def influxdb(
|
|
37
37
|
|
38
38
|
Export:
|
39
39
|
remotive broker export influxdb --url [URL] --output signals.influx --namespace VehicleBus \\
|
40
|
-
--signal Control.SteeringWheel_Position --signal Control.Accelerator_PedalPosition \\
|
41
|
-
--signal GpsPosition.GPS_Longitude --signal GpsPosition.GPS_Latitude
|
40
|
+
--signal vehiclebus:Control.SteeringWheel_Position --signal Control.Accelerator_PedalPosition \\
|
41
|
+
--signal vehiclebus:GpsPosition.GPS_Longitude --signal vehiclebus:GpsPosition.GPS_Latitude
|
42
42
|
|
43
43
|
Output:
|
44
|
-
Control, SteeringWheel_Position=1.0,Accelerator_PedalPosition=0,Speed=0 1664787032944374000
|
45
|
-
GpsPosition, GPS_Longitude=12.982076,GPS_Latitude=55.618748 1664787032948256000
|
44
|
+
Control, namespace:vehiclebus SteeringWheel_Position=1.0,Accelerator_PedalPosition=0,Speed=0 1664787032944374000
|
45
|
+
GpsPosition, namespace:vehiclebus GPS_Longitude=12.982076,GPS_Latitude=55.618748 1664787032948256000
|
46
46
|
|
47
47
|
Import:
|
48
48
|
influx write --org myorg -b my-bucket -p ns --format=lp -f signals.influx
|
@@ -62,10 +62,12 @@ def influxdb(
|
|
62
62
|
signals = list(x)
|
63
63
|
if len(signals) == 0:
|
64
64
|
return
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
sig: str = signals[0]["name"].rpartition(".")[-1]
|
66
|
+
frame = signals[0]["name"].rsplit(".", 1)[0]
|
67
|
+
# frame_name = signals[0]["name"].split(".")[1]
|
68
|
+
namespace = signals[0]["namespace"]
|
69
|
+
signals_str = ",".join(list(map(lambda s: f'{sig}={s["value"]}', signals)))
|
70
|
+
influx_lp = f"{frame},namespace={namespace} {signals_str} {round(signals[0]['timestamp_us'] * 1000)}"
|
69
71
|
if output is not None:
|
70
72
|
f.write(f"{influx_lp}\n")
|
71
73
|
else:
|
@@ -104,7 +106,17 @@ def influxdb(
|
|
104
106
|
# print(namespace)
|
105
107
|
# signals2 = list(map( lambda s: s['signal'], broker.list_signal_names2(namespace)))
|
106
108
|
try:
|
109
|
+
|
110
|
+
def to_subscribable_signal(sig: str):
|
111
|
+
arr = sig.split(":")
|
112
|
+
if len(arr) != 2:
|
113
|
+
ErrorPrinter.print_hint(f"--signal must have format namespace:signal ({sig})")
|
114
|
+
exit(1)
|
115
|
+
|
116
|
+
return SubscribableSignal(namespace=arr[0], name=arr[1])
|
117
|
+
|
118
|
+
signals_to_subscribe_to = list(map(to_subscribable_signal, signal))
|
107
119
|
broker = Broker(url, api_key)
|
108
|
-
broker.
|
120
|
+
broker.long_name_subscribe(signals_to_subscribe_to, per_frame_influx_line_protocol, on_change_only)
|
109
121
|
except grpc.RpcError as rpc_error:
|
110
122
|
ErrorPrinter.print_grpc_error(rpc_error)
|
cli/broker/lib/broker.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import binascii
|
4
|
-
import datetime
|
5
4
|
import ntpath
|
6
5
|
import os
|
7
6
|
import posixpath
|
@@ -183,7 +182,9 @@ class Broker:
|
|
183
182
|
self.__check_playbackmode_result(status)
|
184
183
|
return status
|
185
184
|
|
186
|
-
def listen_on_playback(self, callback: Callable[[str], None]):
|
185
|
+
def listen_on_playback(self, repeat: bool, recording_and_namespace: List, callback: Callable[[int, int, str], None]):
|
186
|
+
# include recording_and_namespace if we want to loop the recording
|
187
|
+
# This can probably be improved
|
187
188
|
def get_mode(mode: int):
|
188
189
|
if mode == 0:
|
189
190
|
return "playing"
|
@@ -195,14 +196,19 @@ class Broker:
|
|
195
196
|
sub = self.traffic_stub.PlayTrafficStatus(br.common_pb2.Empty())
|
196
197
|
for playback_state in sub:
|
197
198
|
p = typing.cast(br.traffic_api_pb2.PlaybackInfos, playback_state)
|
198
|
-
|
199
|
+
offset_length = int(p.playbackInfo[0].playbackMode.offsetTime / 1000000)
|
199
200
|
start_time = p.playbackInfo[0].playbackMode.startTime
|
200
201
|
end_time = p.playbackInfo[0].playbackMode.endTime
|
201
202
|
mode = p.playbackInfo[0].playbackMode.mode
|
202
203
|
|
203
|
-
|
204
|
+
total_length = int((end_time - start_time) / 1000000)
|
204
205
|
|
205
|
-
|
206
|
+
if mode == 2 and repeat:
|
207
|
+
# If we get a stop and is fairly (this is mostly not 100%) close to the end
|
208
|
+
# we repeat the recording when files are included
|
209
|
+
if abs(total_length - offset_length) < 5:
|
210
|
+
self.play(recording_and_namespace)
|
211
|
+
callback(offset_length, total_length, get_mode(mode))
|
206
212
|
|
207
213
|
def pause(self, namespace: str, path: str, silent: bool = False):
|
208
214
|
playback_list = [
|
@@ -478,7 +484,12 @@ class Broker:
|
|
478
484
|
return subscription
|
479
485
|
|
480
486
|
def __each_signal(self, signals, callback):
|
481
|
-
callback(
|
487
|
+
callback(
|
488
|
+
map(
|
489
|
+
lambda s: {"timestamp_us": s.timestamp, "namespace": s.id.namespace.name, "name": s.id.name, "value": self.__get_value(s)},
|
490
|
+
signals,
|
491
|
+
)
|
492
|
+
)
|
482
493
|
|
483
494
|
@staticmethod
|
484
495
|
def __get_value(signal):
|
cli/cloud/recordings_playback.py
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
import datetime
|
3
4
|
import json
|
4
5
|
import tempfile
|
6
|
+
from typing import List
|
5
7
|
|
6
8
|
import grpc
|
9
|
+
import rich
|
7
10
|
import typer
|
8
11
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
9
12
|
|
@@ -25,13 +28,15 @@ def play(
|
|
25
28
|
broker: str = typer.Option(None, help="Broker to use"),
|
26
29
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
27
30
|
show_progress: bool = typer.Option(False, help="Show progress after started playing"),
|
31
|
+
repeat: bool = typer.Option(False, help="Repeat recording - must keep command running in terminal"),
|
28
32
|
):
|
29
33
|
"""
|
30
|
-
Start playing a recording
|
34
|
+
Start playing a recording.
|
35
|
+
There is no problem invoking play multiple times since if it is already playing the command will be ignored.
|
36
|
+
Use --repeat to have the recording replayed when it reaches the end.
|
31
37
|
"""
|
32
|
-
|
33
|
-
|
34
|
-
_do_change_playback_mode("status", recording_session, broker, project)
|
38
|
+
|
39
|
+
_do_change_playback_mode("play", recording_session, broker, project, progress_on_play=show_progress, repeat=repeat)
|
35
40
|
|
36
41
|
|
37
42
|
@app.command()
|
@@ -53,7 +58,8 @@ def progress(
|
|
53
58
|
project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
|
54
59
|
):
|
55
60
|
"""
|
56
|
-
Shows progress of the recording playing
|
61
|
+
Shows progress of the recording playing.
|
62
|
+
Use --repeat to have the recording replayed when it reaches the end.
|
57
63
|
"""
|
58
64
|
_do_change_playback_mode("status", recording_session, broker, project)
|
59
65
|
|
@@ -83,7 +89,15 @@ def stop(
|
|
83
89
|
_do_change_playback_mode("stop", recording_session, broker, project)
|
84
90
|
|
85
91
|
|
86
|
-
def _do_change_playback_mode(
|
92
|
+
def _do_change_playback_mode(
|
93
|
+
mode: str,
|
94
|
+
recording_session: str,
|
95
|
+
broker: str,
|
96
|
+
project: str,
|
97
|
+
seconds: int | None = None,
|
98
|
+
progress_on_play: bool = False,
|
99
|
+
repeat: bool = False,
|
100
|
+
): # noqa: C901
|
87
101
|
response = rest.handle_get(f"/api/project/{project}/files/recording/{recording_session}", return_response=True)
|
88
102
|
r = json.loads(response.text)
|
89
103
|
recordings: list = r["recordings"]
|
@@ -103,6 +117,42 @@ def _do_change_playback_mode(mode: str, recording_session: str, broker: str, pro
|
|
103
117
|
|
104
118
|
broker_info = json.loads(response.text)
|
105
119
|
broker = Broker(broker_info["url"], None)
|
120
|
+
|
121
|
+
_verify_recording_on_broker(broker, recording_session, mode, project)
|
122
|
+
|
123
|
+
if mode == "pause":
|
124
|
+
broker.pause_play(files, True)
|
125
|
+
elif mode == "play":
|
126
|
+
broker.play(files, True)
|
127
|
+
if progress_on_play or repeat:
|
128
|
+
_track_progress(broker, repeat, files)
|
129
|
+
elif mode == "seek":
|
130
|
+
broker.seek(files, int(seconds * 1000000), True)
|
131
|
+
elif mode == "stop":
|
132
|
+
broker.seek(files, 0, True)
|
133
|
+
elif mode == "status":
|
134
|
+
_track_progress(broker, repeat, files)
|
135
|
+
else:
|
136
|
+
raise Exception(f"Illegal command {mode}")
|
137
|
+
|
138
|
+
|
139
|
+
def _track_progress(broker: Broker, repeat: bool, files: List):
|
140
|
+
p = Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True)
|
141
|
+
t = p.add_task("label", total=1)
|
142
|
+
if repeat:
|
143
|
+
rich.print(":point_right: Keep this command running in terminal to keep the recording play with repeat")
|
144
|
+
with p:
|
145
|
+
|
146
|
+
def print_progress(offset: int, total: int, current_mode: str):
|
147
|
+
p.update(
|
148
|
+
t,
|
149
|
+
description=f"{(datetime.timedelta(seconds=offset))} " f"/ {(datetime.timedelta(seconds=total))} ({current_mode})",
|
150
|
+
)
|
151
|
+
|
152
|
+
broker.listen_on_playback(repeat, files, print_progress)
|
153
|
+
|
154
|
+
|
155
|
+
def _verify_recording_on_broker(broker: Broker, recording_session: str, mode: str, project: str):
|
106
156
|
try:
|
107
157
|
# Here we try to verify that we are operating on a recording that is mounted on the
|
108
158
|
# broker so we can verify this before we try playback and can also present some good
|
@@ -126,19 +176,3 @@ def _do_change_playback_mode(mode: str, recording_session: str, broker: str, pro
|
|
126
176
|
else:
|
127
177
|
ErrorPrinter.print_grpc_error(rpc_error)
|
128
178
|
exit(1)
|
129
|
-
if mode == "pause":
|
130
|
-
broker.pause_play(files, True)
|
131
|
-
elif mode == "play":
|
132
|
-
r = broker.play(files, True)
|
133
|
-
elif mode == "seek":
|
134
|
-
broker.seek(files, int(seconds * 1000000), True)
|
135
|
-
elif mode == "stop":
|
136
|
-
broker.seek(files, 0, True)
|
137
|
-
elif mode == "status":
|
138
|
-
p = Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True)
|
139
|
-
t = p.add_task("label", total=1)
|
140
|
-
|
141
|
-
with p:
|
142
|
-
broker.listen_on_playback(lambda c: p.update(t, description=str(c)))
|
143
|
-
else:
|
144
|
-
raise Exception(f"Illegal command {mode}")
|
@@ -1,10 +1,10 @@
|
|
1
1
|
cli/__about__.py,sha256=qXVkxWb3aPCF-4MjQhB0wqL2GEblEH4Qwk70o29UkJk,122
|
2
2
|
cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
cli/broker/brokers.py,sha256=sSX--mm5ln5RUFR60VFHQR6NWu4Qz7Jqi2Ws-4TJsDI,3052
|
4
|
-
cli/broker/export.py,sha256=
|
4
|
+
cli/broker/export.py,sha256=kPB8xRe-I-fte5d95E0Uy2wLvQp943e4yFARGPKySoY,4386
|
5
5
|
cli/broker/files.py,sha256=_8elBjbmJ5MEEeFGJ7QYkXzyuLDCDpO6UvEVXHbRy7U,4133
|
6
6
|
cli/broker/lib/__about__.py,sha256=xnZ5V6ZcHW9dhWLWdMzVjYJbEnMKpeXm0_S_mbNzypE,141
|
7
|
-
cli/broker/lib/broker.py,sha256=
|
7
|
+
cli/broker/lib/broker.py,sha256=kpcSaHcjZlcsgNgxwDBEa13RYRtx0Uz-fbBg2pKAHI0,21174
|
8
8
|
cli/broker/playback.py,sha256=oOfC8Jn4Ib-nc9T6ob_uNXZSeCWfft7MrMQPafH4U2I,4846
|
9
9
|
cli/broker/record.py,sha256=gEvo3myHbIl6UyXzhJE741NiwRrFf7doBg6HXzzp5z0,1382
|
10
10
|
cli/broker/scripting.py,sha256=sLDtuktWsVk0fJ3RW4kYyh-_YAVJP3VM0xFIQR499Oo,3392
|
@@ -17,7 +17,7 @@ cli/cloud/cloud_cli.py,sha256=nRRXFF_IgUMayerxS-h7oqsNe6tt34Q5VeThq8gatEg,1443
|
|
17
17
|
cli/cloud/configs.py,sha256=2p1mCHf5BwYNtwbY0Cbed5t6-79WHGKWU4Fv6LuJ21o,4069
|
18
18
|
cli/cloud/projects.py,sha256=-uqltAOficwprOKaPd2R0Itm4sqTz3VJNs9Sc8jtO5k,1369
|
19
19
|
cli/cloud/recordings.py,sha256=0xQw8iv-NjUUqxQjq5O4WqNYsew2lNlQ9Z0Qevl2f9E,20844
|
20
|
-
cli/cloud/recordings_playback.py,sha256=
|
20
|
+
cli/cloud/recordings_playback.py,sha256=ygJRBbJ_BYmrEZ7k0j2b11N3xrrgmOdL3jZckJ-f-uU,7120
|
21
21
|
cli/cloud/rest_helper.py,sha256=g7lmGosAS0IDlo9Aso0bH0tdlLTCz0IYx3R71DXKtkc,6491
|
22
22
|
cli/cloud/sample_recordings.py,sha256=g1X6JTxvzWInSP9R1BJsDmL4WqvpEKqjdJR_xT4bo1U,639
|
23
23
|
cli/cloud/service_account_tokens.py,sha256=7vjoMd6Xq7orWCUP7TVUVa86JA0OiX8O10NZcHUE6rM,2294
|
@@ -33,8 +33,8 @@ cli/tools/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
33
33
|
cli/tools/can/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
34
34
|
cli/tools/can/can.py,sha256=kSd1c-nxxXyeKkm19oDILiDBZsKOcpjsUT0T3xox5Qs,2172
|
35
35
|
cli/tools/tools.py,sha256=LwQdWMcJ19pCyKUsVfSB2B3R6ui61NxxFWP0Nrnd5Jk,198
|
36
|
-
remotivelabs_cli-0.0.
|
37
|
-
remotivelabs_cli-0.0.
|
38
|
-
remotivelabs_cli-0.0.
|
39
|
-
remotivelabs_cli-0.0.
|
40
|
-
remotivelabs_cli-0.0.
|
36
|
+
remotivelabs_cli-0.0.16.dist-info/LICENSE,sha256=qDPP_yfuv1fF-u7EfexN-cN3M8aFgGVndGhGLovLKz0,608
|
37
|
+
remotivelabs_cli-0.0.16.dist-info/METADATA,sha256=A5ZA5VGXfw287Q187R5Pt4cPH5xOVmDWzeRq8offtPk,1224
|
38
|
+
remotivelabs_cli-0.0.16.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
39
|
+
remotivelabs_cli-0.0.16.dist-info/entry_points.txt,sha256=lvDhPgagLqW_KTnLPCwKSqfYlEp-1uYVosRiPjsVj10,45
|
40
|
+
remotivelabs_cli-0.0.16.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|