bluer-ugv 7.790.1__py3-none-any.whl → 7.819.1__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.

Potentially problematic release.


This version of bluer-ugv might be problematic. Click here for more details.

Files changed (33) hide show
  1. bluer_ugv/.abcli/alias.sh +0 -2
  2. bluer_ugv/.abcli/swallow/ethernet/test.sh +12 -0
  3. bluer_ugv/.abcli/swallow/ethernet.sh +15 -0
  4. bluer_ugv/.abcli/tests/help.sh +2 -0
  5. bluer_ugv/README/aliases.py +23 -0
  6. bluer_ugv/README/docs.py +2 -2
  7. bluer_ugv/README/root.py +4 -0
  8. bluer_ugv/README/swallow/digital/design/__init__.py +2 -0
  9. bluer_ugv/README/swallow/digital/design/ethernet.py +5 -0
  10. bluer_ugv/README/ugvs/__main__.py +0 -7
  11. bluer_ugv/README/ugvs/db.py +1 -1
  12. bluer_ugv/README/ugvs/ethernet.py +55 -0
  13. bluer_ugv/README/ugvs/get.py +0 -4
  14. bluer_ugv/__init__.py +1 -1
  15. bluer_ugv/config.env +3 -1
  16. bluer_ugv/env.py +2 -0
  17. bluer_ugv/help/get.py +1 -6
  18. bluer_ugv/help/swallow/__init__.py +2 -0
  19. bluer_ugv/help/swallow/ethernet.py +34 -0
  20. bluer_ugv/swallow/session/classical/ethernet/__init__.py +0 -0
  21. bluer_ugv/swallow/session/classical/ethernet/__main__.py +42 -0
  22. bluer_ugv/swallow/session/classical/ethernet/classes.py +52 -0
  23. bluer_ugv/swallow/session/classical/ethernet/client.py +278 -0
  24. bluer_ugv/swallow/session/classical/ethernet/command.py +22 -0
  25. bluer_ugv/swallow/session/classical/ethernet/testing.py +52 -0
  26. bluer_ugv/swallow/session/classical/keyboard/classes.py +19 -0
  27. bluer_ugv/swallow/session/classical/session.py +6 -0
  28. {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.819.1.dist-info}/METADATA +4 -5
  29. {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.819.1.dist-info}/RECORD +32 -21
  30. bluer_ugv/README/alias.py +0 -10
  31. {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.819.1.dist-info}/WHEEL +0 -0
  32. {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.819.1.dist-info}/licenses/LICENSE +0 -0
  33. {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.819.1.dist-info}/top_level.txt +0 -0
bluer_ugv/.abcli/alias.sh CHANGED
@@ -1,7 +1,5 @@
1
1
  #! /usr/bin/env bash
2
2
 
3
- alias @rangin=bluer_ugv_rangin
4
-
5
3
  alias @swallow=bluer_ugv_swallow
6
4
 
7
5
  alias @ugv=bluer_ugv
@@ -0,0 +1,12 @@
1
+ #! /usr/bin/env bash
2
+
3
+ function bluer_ugv_swallow_ethernet_test() {
4
+ local options=$1
5
+
6
+ bluer_ai_eval ,$options \
7
+ sudo -E \
8
+ $(which python3) \
9
+ -m bluer_ugv.swallow.session.classical.ethernet \
10
+ test \
11
+ "${@:2}"
12
+ }
@@ -0,0 +1,15 @@
1
+ #! /usr/bin/env bash
2
+
3
+ function bluer_ugv_swallow_ethernet() {
4
+ local task=$1
5
+
6
+ local function_name=bluer_ugv_swallow_ethernet_$task
7
+ if [[ $(type -t $function_name) == "function" ]]; then
8
+ $function_name "${@:2}"
9
+ return
10
+ fi
11
+
12
+ python3 -m bluer_ugv.swallow.session.classical.ethernet "$@"
13
+ }
14
+
15
+ bluer_ai_source_caller_suffix_path /ethernet
@@ -20,6 +20,8 @@ function test_bluer_ugv_help() {
20
20
  "@swallow env list" \
21
21
  "@swallow env set" \
22
22
  \
23
+ "@swallow ethernet" \
24
+ \
23
25
  "@swallow git" \
24
26
  "@swallow git rm_keys" \
25
27
  \
@@ -0,0 +1,23 @@
1
+ from bluer_objects.README.alias import list_of_aliases
2
+
3
+ from bluer_ugv import NAME
4
+
5
+ docs = [
6
+ {
7
+ "path": "../docs/aliases",
8
+ "macros": {
9
+ "aliases:::": list_of_aliases(
10
+ NAME,
11
+ itemized=True,
12
+ ),
13
+ },
14
+ }
15
+ ] + [
16
+ {
17
+ "path": f"../docs/aliases/{alias_name}.md",
18
+ }
19
+ for alias_name in list_of_aliases(
20
+ NAME,
21
+ as_markdown=False,
22
+ )
23
+ ]
bluer_ugv/README/docs.py CHANGED
@@ -4,7 +4,7 @@ from bluer_ugv.README.fire import docs as fire_docs
4
4
  from bluer_ugv.README.rangin import docs as rangin_docs
5
5
  from bluer_ugv.README.ravin import docs as ravin_docs
6
6
  from bluer_ugv.README import (
7
- alias,
7
+ aliases,
8
8
  beast,
9
9
  root,
10
10
  releases,
@@ -16,7 +16,7 @@ from bluer_ugv.README.validations import docs as validations
16
16
 
17
17
  docs = (
18
18
  root.docs
19
- + alias.docs
19
+ + aliases.docs
20
20
  + arzhang_docs.docs
21
21
  + beast.docs
22
22
  + eagle_docs.docs
bluer_ugv/README/root.py CHANGED
@@ -1,3 +1,6 @@
1
+ from bluer_objects.README.alias import list_of_aliases
2
+
3
+ from bluer_ugv import NAME
1
4
  from bluer_ugv.README.items import items
2
5
  from bluer_ugv.README.shortcuts import items as shortcuts_items
3
6
 
@@ -8,6 +11,7 @@ docs = [
8
11
  "items": items,
9
12
  "macros": {
10
13
  "shortcuts:::": shortcuts_items,
14
+ "aliases:::": list_of_aliases(NAME),
11
15
  },
12
16
  },
13
17
  {
@@ -5,6 +5,7 @@ from bluer_ugv.README.swallow.consts import (
5
5
  swallow_electrical_designs,
6
6
  )
7
7
  from bluer_ugv.README.swallow.digital.design import (
8
+ ethernet,
8
9
  mechanical,
9
10
  parts,
10
11
  ultrasonic_sensor,
@@ -61,6 +62,7 @@ docs = (
61
62
  ),
62
63
  },
63
64
  ]
65
+ + ethernet.docs
64
66
  + mechanical.docs
65
67
  + parts.docs
66
68
  + ultrasonic_sensor.docs
@@ -0,0 +1,5 @@
1
+ docs = [
2
+ {
3
+ "path": "../docs/swallow/digital/design/ethernet.md",
4
+ },
5
+ ]
@@ -23,12 +23,6 @@ parser.add_argument(
23
23
  "--what",
24
24
  type=str,
25
25
  )
26
- parser.add_argument(
27
- "--include_comments",
28
- type=int,
29
- default=0,
30
- help="0 | 1",
31
- )
32
26
  args = parser.parse_args()
33
27
 
34
28
  success = False
@@ -38,7 +32,6 @@ if args.task == "get":
38
32
  get(
39
33
  ugv_name=args.ugv_name,
40
34
  what=args.what,
41
- include_comments=args.include_comments == 1,
42
35
  )
43
36
  )
44
37
  else:
@@ -19,7 +19,7 @@ dict_of_ugvs = {
19
19
  "tagline": "the first one.",
20
20
  "class": "swallow",
21
21
  "computers": {
22
- "front": "swallow2 (`swallow` was used for Ubuntu experiments)",
22
+ "front": "swallow2", # `swallow` was used for Ubuntu experiments
23
23
  },
24
24
  },
25
25
  "arzhang": {
@@ -0,0 +1,55 @@
1
+ from typing import Tuple
2
+
3
+ from bluer_ugv.README.ugvs.db import dict_of_ugvs
4
+ from bluer_ugv.logger import logger
5
+
6
+
7
+ def find_server(
8
+ hostname: str,
9
+ ) -> Tuple[
10
+ bool, # success
11
+ bool, # is_server
12
+ str, # "0.0.0.0" if is_server else f"{server_name}.local"
13
+ ]:
14
+ for ugv_name, info in dict_of_ugvs.items():
15
+ found: bool = False
16
+ dict_of_computers = info.get("computers", {})
17
+ assert isinstance(dict_of_computers, dict)
18
+
19
+ for location, hostname_ in dict_of_computers.items():
20
+ if hostname != hostname_:
21
+ continue
22
+
23
+ logger.info(f"{hostname} == {ugv_name}.{location}")
24
+ found = True
25
+
26
+ if location == "front":
27
+ return True, True, "0.0.0.0"
28
+
29
+ if not found:
30
+ continue
31
+
32
+ list_of_server_names = [
33
+ hostname_
34
+ for location, hostname_ in dict_of_computers.items()
35
+ if location == "front"
36
+ ]
37
+ if not list_of_server_names:
38
+ logger.error(f"no server (.front) onboard {ugv_name}.")
39
+ return False, False, ""
40
+
41
+ if len(list_of_server_names) > 1:
42
+ logger.warning(
43
+ "{} servers (.front) onboard {}".format(
44
+ len(list_of_server_names),
45
+ ugv_name,
46
+ )
47
+ )
48
+
49
+ server_name = list_of_server_names[0]
50
+ logger.info(f"{hostname}: server_name={server_name}")
51
+
52
+ return True, False, f"{server_name}.local"
53
+
54
+ logger.error(f"cannot find hostname: {hostname}.")
55
+ return False, False, ""
@@ -4,7 +4,6 @@ from bluer_ugv.README.ugvs.db import dict_of_ugvs
4
4
  def get(
5
5
  ugv_name: str,
6
6
  what: str,
7
- include_comments: bool = False,
8
7
  ) -> str:
9
8
  list_of_what = what.split(".")
10
9
  info = dict_of_ugvs.get(ugv_name, {})
@@ -17,7 +16,4 @@ def get(
17
16
  if not info:
18
17
  info = "not-found"
19
18
 
20
- if not include_comments and info:
21
- info = info.split(" ")[0]
22
-
23
19
  return str(info)
bluer_ugv/__init__.py CHANGED
@@ -4,7 +4,7 @@ ICON = "🐬"
4
4
 
5
5
  DESCRIPTION = f"{ICON} AI x UGV."
6
6
 
7
- VERSION = "7.790.1"
7
+ VERSION = "7.819.1"
8
8
 
9
9
  REPO_NAME = "bluer-ugv"
10
10
 
bluer_ugv/config.env CHANGED
@@ -36,4 +36,6 @@ BLUER_UGV_RELEASE_2=release-two-2025-12-01-12-50-03-1hyrts
36
36
  BLUER_UGV_AUDIO_LANGUAGE=fa
37
37
  BLUER_UGV_AUDIO_CHANNELS=1
38
38
  BLUER_UGV_AUDIO_RATE=48000
39
- BLUER_UGV_AUDIO_LENGTH=30
39
+ BLUER_UGV_AUDIO_LENGTH=30
40
+
41
+ BLUER_UGV_ETHERNET_PORT=5050
bluer_ugv/env.py CHANGED
@@ -105,3 +105,5 @@ BLUER_UGV_AUDIO_LENGTH = get_env(
105
105
  "BLUER_UGV_AUDIO_LENGTH",
106
106
  30,
107
107
  )
108
+
109
+ BLUER_UGV_ETHERNET_PORT = get_env("BLUER_UGV_ETHERNET_PORT", 5050)
bluer_ugv/help/get.py CHANGED
@@ -7,18 +7,13 @@ def help_get(
7
7
  tokens: List[str],
8
8
  mono: bool,
9
9
  ) -> str:
10
- args = [
11
- "[--include_comments 1]",
12
- ]
13
-
14
10
  return show_usage(
15
11
  [
16
12
  "@ugv",
17
13
  "get",
18
14
  "<ugv-name>",
19
15
  "computers.back | computers.front | computers.top | <what>",
20
- ]
21
- + args,
16
+ ],
22
17
  "get ugv info.",
23
18
  mono=mono,
24
19
  )
@@ -1,6 +1,7 @@
1
1
  from bluer_ugv.help.swallow.dataset import help_functions as help_dataset
2
2
  from bluer_ugv.help.swallow.debug import help_debug
3
3
  from bluer_ugv.help.swallow.env import help_functions as help_env
4
+ from bluer_ugv.help.swallow.ethernet import help_functions as help_ethernet
4
5
  from bluer_ugv.help.swallow.git import help_functions as help_git
5
6
  from bluer_ugv.help.swallow.keyboard import help_functions as help_keyboard
6
7
  from bluer_ugv.help.swallow.select_target import help_select_target
@@ -13,6 +14,7 @@ help_functions = {
13
14
  "dataset": help_dataset,
14
15
  "debug": help_debug,
15
16
  "env": help_env,
17
+ "ethernet": help_ethernet,
16
18
  "git": help_git,
17
19
  "keyboard": help_keyboard,
18
20
  "select_target": help_select_target,
@@ -0,0 +1,34 @@
1
+ from typing import List
2
+
3
+ from bluer_options.terminal import show_usage, xtra
4
+
5
+ from bluer_ugv import env
6
+
7
+
8
+ def help_test(
9
+ tokens: List[str],
10
+ mono: bool,
11
+ ) -> str:
12
+ options = xtra("dryrun", mono=mono)
13
+
14
+ args = [
15
+ "[--is_server 0 | 1]",
16
+ "[--server_name 0.0.0.0 | <server_name>.local]",
17
+ ]
18
+
19
+ return show_usage(
20
+ [
21
+ "@swallow",
22
+ "ethernet",
23
+ "test",
24
+ f"[{options}]",
25
+ ]
26
+ + args,
27
+ "test ethernet.",
28
+ mono=mono,
29
+ )
30
+
31
+
32
+ help_functions = {
33
+ "test": help_test,
34
+ }
@@ -0,0 +1,42 @@
1
+ import argparse
2
+
3
+ from blueness import module
4
+ from blueness.argparse.generic import sys_exit
5
+
6
+ from bluer_ugv import env
7
+ from bluer_ugv import NAME
8
+ from bluer_ugv.swallow.session.classical.ethernet.testing import test
9
+ from bluer_ugv.logger import logger
10
+
11
+ NAME = module.name(__file__, NAME)
12
+
13
+ parser = argparse.ArgumentParser(NAME)
14
+ parser.add_argument(
15
+ "task",
16
+ type=str,
17
+ help="test",
18
+ )
19
+ parser.add_argument(
20
+ "--is_server",
21
+ type=int,
22
+ default=1,
23
+ help="0 | 1",
24
+ )
25
+ parser.add_argument(
26
+ "--server_name",
27
+ type=str,
28
+ default="0.0.0.0",
29
+ help="0.0.0.0 | <server_name>.local",
30
+ )
31
+ args = parser.parse_args()
32
+
33
+ success = False
34
+ if args.task == "test":
35
+ success = test(
36
+ server_name=args.server_name,
37
+ is_server=args.is_server == 1,
38
+ )
39
+ else:
40
+ success = None
41
+
42
+ sys_exit(logger, NAME, args.task, success)
@@ -0,0 +1,52 @@
1
+ import threading
2
+
3
+ from bluer_options.env import abcli_hostname
4
+
5
+ from bluer_ugv import env
6
+ from bluer_ugv.swallow.session.classical.ethernet.client import EthernetClient
7
+ from bluer_ugv.README.ugvs.ethernet import find_server
8
+ from bluer_ugv.logger import logger
9
+
10
+
11
+ class ClassicalEthernet:
12
+ def __init__(
13
+ self,
14
+ ):
15
+ self.enabled: bool = True
16
+
17
+ logger.info(f"creating {self.__class__.__name__}...")
18
+
19
+ self.running = False
20
+
21
+ self.enabled, is_server, server_name = find_server(hostname=abcli_hostname)
22
+ if not self.enabled:
23
+ return
24
+
25
+ self.client = EthernetClient(
26
+ host=server_name,
27
+ port=env.BLUER_UGV_ETHERNET_PORT,
28
+ is_server=is_server,
29
+ )
30
+
31
+ self.running = True
32
+ self.thread = threading.Thread(target=self.loop, daemon=True)
33
+ self.thread.start()
34
+
35
+ def stop(self):
36
+ if not self.enabled:
37
+ return
38
+
39
+ self.running = False
40
+ self.thread.join()
41
+
42
+ self.client.close()
43
+ logger.info(f"{self.__class__.__name__}.stopped.")
44
+
45
+ def loop(self):
46
+ logger.info(f"{self.__class__.__name__}.loop started.")
47
+
48
+ while self.running:
49
+ self.client.process()
50
+
51
+ def update(self):
52
+ return not self.client.stop_received
@@ -0,0 +1,278 @@
1
+ import json
2
+ from queue import Empty, SimpleQueue
3
+ import socket
4
+ from typing import Tuple, Dict, Optional
5
+ import struct
6
+ import threading
7
+ import time
8
+
9
+ from bluer_sbc.session.functions import reply_to_bash
10
+
11
+ from bluer_ugv.logger import logger
12
+ from bluer_ugv.swallow.session.classical.ethernet.command import EthernetCommand
13
+ from bluer_ugv.logger import logger
14
+
15
+
16
+ class EthernetClient:
17
+ def __init__(
18
+ self,
19
+ host: str,
20
+ port: int,
21
+ is_server: bool = False,
22
+ reconnect_sec: float = 1.0,
23
+ ):
24
+ self.stop_received: bool = False
25
+
26
+ self.host = host
27
+ self.port = port
28
+ self.is_server = is_server
29
+ self.reconnect_sec = reconnect_sec
30
+
31
+ self._send_queue: SimpleQueue[EthernetCommand] = SimpleQueue()
32
+
33
+ self._lock = threading.Lock()
34
+ self._sock: Optional[socket.socket] = None
35
+ self._listener: Optional[socket.socket] = None
36
+
37
+ logger.info(
38
+ "{} created: host={}, port={}{}.".format(
39
+ self.__class__.__name__,
40
+ self.host,
41
+ self.port,
42
+ ", server" if is_server else "",
43
+ )
44
+ )
45
+
46
+ def _close_sockets(self):
47
+ with self._lock:
48
+ if self._sock:
49
+ try:
50
+ self._sock.close()
51
+ logger.info(f"{self.__class__.__name__}._sock closed.")
52
+ except Exception as e:
53
+ logger.warning(e)
54
+ self._sock = None
55
+
56
+ if self._listener:
57
+ try:
58
+ self._listener.close()
59
+ logger.info(f"{self.__class__.__name__}._listener closed.")
60
+ except Exception as e:
61
+ logger.warning(e)
62
+ self._listener = None
63
+
64
+ def _drain_send_queue(self) -> bool:
65
+ with self._lock:
66
+ sock = self._sock
67
+ if sock is None:
68
+ return False
69
+
70
+ while True:
71
+ try:
72
+ cmd = self._send_queue.get_nowait()
73
+ except Empty:
74
+ return True
75
+
76
+ try:
77
+ payload = cmd.to_dict()
78
+ # Sending can block; keep it short and safe
79
+ sock.setblocking(True)
80
+ try:
81
+ self._send_dict(sock, payload)
82
+ finally:
83
+ sock.setblocking(False)
84
+
85
+ logger.info(f"{self.__class__.__name__}: sent {cmd.as_str()}")
86
+
87
+ except (ConnectionError, OSError) as e:
88
+ logger.warning(f"{self.__class__.__name__}: send error: {e}")
89
+ self._close_sockets()
90
+ # put back? your choice; here we drop to avoid infinite resend
91
+ return False
92
+ except Exception as e:
93
+ logger.error(e)
94
+ return False
95
+
96
+ return True
97
+
98
+ def _ensure_connection(self) -> bool:
99
+ """
100
+ Ensure self._sock is a connected TCP socket.
101
+ Returns True if connected.
102
+ """
103
+ with self._lock:
104
+ if self._sock is not None:
105
+ return True
106
+
107
+ try:
108
+ if self.is_server:
109
+ # Lazy-create listener
110
+ with self._lock:
111
+ if self._listener is None:
112
+ lst = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
113
+ lst.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
114
+ lst.bind((self.host, self.port))
115
+ lst.listen(1)
116
+ lst.settimeout(1.0) # so thread can exit cleanly
117
+ self._listener = lst
118
+ logger.info(
119
+ f"{self.__class__.__name__}: listening on {self.host}:{self.port}"
120
+ )
121
+
122
+ # Accept
123
+ try:
124
+ conn, addr = self._listener.accept()
125
+ except socket.timeout:
126
+ logger.warning("socket timeout.")
127
+ return False
128
+ except Exception as e:
129
+ logger.error(e)
130
+ return False
131
+
132
+ conn.setblocking(False)
133
+ with self._lock:
134
+ self._sock = conn
135
+ logger.info(f"{self.__class__.__name__}: accepted {addr}")
136
+
137
+ else:
138
+ # Client connect
139
+ sock = socket.create_connection((self.host, self.port), timeout=2.0)
140
+ sock.setblocking(False)
141
+ with self._lock:
142
+ self._sock = sock
143
+ logger.info(
144
+ f"{self.__class__.__name__}: connected to {self.host}:{self.port}"
145
+ )
146
+
147
+ return True
148
+
149
+ except Exception as e:
150
+ logger.warning(f"{self.__class__.__name__}: connection failed: {e}")
151
+ self._close_sockets()
152
+ return False
153
+
154
+ def _recv_dict_blocking(
155
+ self,
156
+ sock: socket.socket,
157
+ ) -> Dict:
158
+ header = self._recv_exact(sock, 4)
159
+ msg_len = struct.unpack("!I", header)[0]
160
+ raw = self._recv_exact(sock, msg_len)
161
+ return json.loads(raw.decode("utf-8"))
162
+
163
+ @staticmethod
164
+ def _recv_exact(
165
+ sock: socket.socket,
166
+ n: int,
167
+ ) -> bytes:
168
+ buf = b""
169
+ while len(buf) < n:
170
+ chunk = sock.recv(n - len(buf))
171
+ if not chunk:
172
+ raise ConnectionError("socket closed")
173
+ buf += chunk
174
+ return buf
175
+
176
+ @staticmethod
177
+ def _send_dict(
178
+ sock: socket.socket,
179
+ payload: Dict,
180
+ ) -> None:
181
+ raw = json.dumps(payload).encode("utf-8")
182
+ header = struct.pack("!I", len(raw))
183
+ sock.sendall(header + raw)
184
+
185
+ def _try_recv_one(self) -> Tuple[bool, EthernetCommand]:
186
+ """
187
+ Non-blocking receive of exactly one command.
188
+ Uses a small state machine via MSG_PEEK not needed: instead we temporarily
189
+ switch to blocking only when we know bytes are ready.
190
+ """
191
+ with self._lock:
192
+ sock = self._sock
193
+ if sock is None:
194
+ return False, EthernetCommand()
195
+
196
+ try:
197
+ # Peek header availability (4 bytes)
198
+ try:
199
+ hdr = sock.recv(4, socket.MSG_PEEK)
200
+ except BlockingIOError:
201
+ return False, EthernetCommand()
202
+
203
+ if len(hdr) < 4:
204
+ return False, EthernetCommand()
205
+
206
+ msg_len = struct.unpack("!I", hdr)[0]
207
+ total = 4 + msg_len
208
+
209
+ try:
210
+ blob = sock.recv(total, socket.MSG_PEEK)
211
+ except BlockingIOError:
212
+ return False, EthernetCommand()
213
+
214
+ if len(blob) < total:
215
+ return False, EthernetCommand()
216
+
217
+ # Now actually read for real (blocking reads are safe because we already peeked)
218
+ sock.setblocking(True)
219
+ try:
220
+ d = self._recv_dict_blocking(sock)
221
+ finally:
222
+ sock.setblocking(False)
223
+
224
+ return True, EthernetCommand.from_dict(d)
225
+
226
+ except (ConnectionError, OSError) as e:
227
+ logger.warning(f"{self.__class__.__name__}: recv error: {e}")
228
+ self._close_sockets()
229
+ return False, EthernetCommand()
230
+ except Exception as e:
231
+ logger.warning(f"{self.__class__.__name__}: recv parse error: {e}")
232
+ return False, EthernetCommand()
233
+
234
+ def close(self):
235
+ self._close_sockets()
236
+
237
+ def process(self):
238
+ connected = self._ensure_connection()
239
+ if not connected:
240
+ time.sleep(self.reconnect_sec)
241
+ return
242
+
243
+ # 1) receive at most one per tick (cheap + predictable)
244
+ received, command = self._try_recv_one()
245
+ if received:
246
+ logger.info(
247
+ "{} received {}".format(
248
+ self.__class__.__name__,
249
+ command.as_str(),
250
+ )
251
+ )
252
+
253
+ if command.action == "keyboard":
254
+ reply_to_bash(command.data.get("event", "unknown"))
255
+ self.stop_received = True
256
+ logger.info("stop received.")
257
+
258
+ # 2) drain outbound queue
259
+ self._drain_send_queue()
260
+
261
+ time.sleep(0.1)
262
+
263
+ def send(
264
+ self,
265
+ command: EthernetCommand,
266
+ drain: bool = False,
267
+ ):
268
+ self._send_queue.put(command)
269
+
270
+ logger.info(
271
+ "{}.send: queue += {}".format(
272
+ self.__class__.__name__,
273
+ command.as_str(),
274
+ )
275
+ )
276
+
277
+ if drain:
278
+ self._drain_send_queue()
@@ -0,0 +1,22 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass, field
3
+ from typing import Dict
4
+
5
+
6
+ @dataclass
7
+ class EthernetCommand:
8
+ action: str = ""
9
+ data: Dict = field(default_factory=dict)
10
+
11
+ def as_str(self) -> str:
12
+ return f"{self.__class__.__name__}({self.action})[{self.data}]"
13
+
14
+ def to_dict(self) -> Dict:
15
+ return {"action": self.action, "data": self.data}
16
+
17
+ @staticmethod
18
+ def from_dict(d: Dict) -> "EthernetCommand":
19
+ return EthernetCommand(
20
+ action=str(d.get("action", "")),
21
+ data=dict(d.get("data", {}) or {}),
22
+ )
@@ -0,0 +1,52 @@
1
+ import keyboard
2
+
3
+ from blueness import module
4
+
5
+ from bluer_ugv import NAME
6
+ from bluer_ugv import env
7
+ from bluer_ugv.swallow.session.classical.ethernet.client import EthernetClient
8
+ from bluer_ugv.swallow.session.classical.ethernet.command import EthernetCommand
9
+ from bluer_ugv.logger import logger
10
+
11
+
12
+ NAME = module.name(__file__, NAME)
13
+
14
+
15
+ def test(
16
+ server_name: str,
17
+ is_server: bool,
18
+ port: int = env.BLUER_UGV_ETHERNET_PORT,
19
+ ) -> bool:
20
+ success = True
21
+
22
+ logger.info(
23
+ "{}.test: server_name={}, is_server={}, port={}".format(
24
+ NAME,
25
+ server_name,
26
+ is_server,
27
+ port,
28
+ )
29
+ )
30
+
31
+ client = EthernetClient(
32
+ host=server_name,
33
+ port=port,
34
+ is_server=is_server,
35
+ )
36
+
37
+ logger.info("press 5 to send a message.")
38
+ try:
39
+ while True:
40
+ client.process()
41
+
42
+ if keyboard.is_pressed("5"):
43
+ client.send(EthernetCommand(action="hello"))
44
+ except KeyboardInterrupt:
45
+ logger.info("Ctrl+C, stopping.")
46
+ except Exception as e:
47
+ logger.error(e)
48
+ success = False
49
+
50
+ client.close()
51
+
52
+ return success
@@ -2,9 +2,12 @@ import keyboard
2
2
  import threading
3
3
  from typing import Any, Dict
4
4
 
5
+ from bluer_options.env import abcli_hostname
5
6
  from bluer_sbc.session.functions import reply_to_bash
6
7
  from bluer_algo.socket.connection import DEV_HOST
7
8
 
9
+ from bluer_ugv.swallow.session.classical.ethernet.classes import ClassicalEthernet
10
+ from bluer_ugv.swallow.session.classical.ethernet.command import EthernetCommand
8
11
  from bluer_ugv.swallow.session.classical.keyboard.keys import ControlKeys
9
12
  from bluer_ugv.swallow.session.classical.leds import ClassicalLeds
10
13
  from bluer_ugv.swallow.session.classical.mode import OperationMode
@@ -16,6 +19,7 @@ from bluer_ugv.logger import logger
16
19
  class ClassicalKeyboard:
17
20
  def __init__(
18
21
  self,
22
+ ethernet: ClassicalEthernet,
19
23
  leds: ClassicalLeds,
20
24
  setpoint: ClassicalSetPoint,
21
25
  ):
@@ -23,6 +27,8 @@ class ClassicalKeyboard:
23
27
 
24
28
  self.keys = ControlKeys()
25
29
 
30
+ self.ethernet = ethernet
31
+
26
32
  self.leds = leds
27
33
 
28
34
  self.last_key: str = ""
@@ -53,6 +59,19 @@ class ClassicalKeyboard:
53
59
  if self.special_key:
54
60
  for key, event in self.keys.special_keys.items():
55
61
  if keyboard.is_pressed(key):
62
+ if self.ethernet.enabled and self.ethernet.client.is_server:
63
+ self.ethernet.client.send(
64
+ EthernetCommand(
65
+ action="keyboard",
66
+ data={
67
+ "sender": abcli_hostname,
68
+ "key": key,
69
+ "event": event,
70
+ },
71
+ ),
72
+ drain=True,
73
+ )
74
+
56
75
  reply_to_bash(event)
57
76
  return False
58
77
 
@@ -6,6 +6,7 @@ from bluer_objects.env import abcli_object_name
6
6
  from bluer_objects.metadata import post_to_object
7
7
  from bluer_sbc.env import BLUER_SBC_CAMERA_KIND, BLUER_SBC_SWALLOW_HAS_STEERING
8
8
 
9
+ from bluer_ugv.swallow.session.classical.ethernet.classes import ClassicalEthernet
9
10
  from bluer_ugv.swallow.session.classical.camera import (
10
11
  ClassicalCamera,
11
12
  ClassicalNavigationCamera,
@@ -43,6 +44,8 @@ class ClassicalSession:
43
44
 
44
45
  GPIO.setmode(GPIO.BCM)
45
46
 
47
+ self.ethernet = ClassicalEthernet()
48
+
46
49
  self.leds = ClassicalLeds()
47
50
 
48
51
  self.audio = ClassicalAudio(
@@ -60,6 +63,7 @@ class ClassicalSession:
60
63
  )
61
64
 
62
65
  self.keyboard = ClassicalKeyboard(
66
+ ethernet=self.ethernet,
63
67
  leds=self.leds,
64
68
  setpoint=self.setpoint,
65
69
  )
@@ -142,6 +146,7 @@ class ClassicalSession:
142
146
 
143
147
  def cleanup(self):
144
148
  self.audio.stop()
149
+ self.ethernet.stop()
145
150
  self.ultrasonic_sensor.stop()
146
151
  self.camera.stop()
147
152
 
@@ -194,6 +199,7 @@ class ClassicalSession:
194
199
  self.timing.start("session.update")
195
200
 
196
201
  for thing in [
202
+ self.ethernet,
197
203
  self.keyboard,
198
204
  self.push_button,
199
205
  self.camera,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bluer_ugv
3
- Version: 7.790.1
3
+ Version: 7.819.1
4
4
  Summary: 🐬 AI x UGV.
5
5
  Home-page: https://github.com/kamangir/bluer-ugv
6
6
  Author: Arash Abadpour (Kamangir)
@@ -53,9 +53,8 @@ pip install bluer_ugv
53
53
 
54
54
  ## aliases
55
55
 
56
- [@rangin](https://github.com/kamangir/bluer-ugv/blob/main/bluer_ugv/docs/aliases/rangin.md),
57
- [@swallow](https://github.com/kamangir/bluer-ugv/blob/main/bluer_ugv/docs/aliases/swallow.md),
58
- [@ugv](https://github.com/kamangir/bluer-ugv/blob/main/bluer_ugv/docs/aliases/ugv.md).
56
+ [@swallow](https://github.com/kamangir/bluer-ugv/blob/main/bluer_ugv/docs/aliases/swallow.md)
57
+ [@ugv](https://github.com/kamangir/bluer-ugv/blob/main/bluer_ugv/docs/aliases/ugv.md)
59
58
 
60
59
  ---
61
60
 
@@ -66,7 +65,7 @@ pip install bluer_ugv
66
65
 
67
66
  [![pylint](https://github.com/kamangir/bluer-ugv/actions/workflows/pylint.yml/badge.svg)](https://github.com/kamangir/bluer-ugv/actions/workflows/pylint.yml) [![pytest](https://github.com/kamangir/bluer-ugv/actions/workflows/pytest.yml/badge.svg)](https://github.com/kamangir/bluer-ugv/actions/workflows/pytest.yml) [![bashtest](https://github.com/kamangir/bluer-ugv/actions/workflows/bashtest.yml/badge.svg)](https://github.com/kamangir/bluer-ugv/actions/workflows/bashtest.yml) [![PyPI version](https://img.shields.io/pypi/v/bluer-ugv.svg)](https://pypi.org/project/bluer-ugv/) [![PyPI - Downloads](https://img.shields.io/pypi/dd/bluer-ugv)](https://pypistats.org/packages/bluer-ugv)
68
67
 
69
- built by 🌀 [`bluer README`](https://github.com/kamangir/bluer-objects/tree/main/bluer_objects/README), based on 🐬 [`bluer_ugv-7.790.1`](https://github.com/kamangir/bluer-ugv).
68
+ built by 🌀 [`bluer README`](https://github.com/kamangir/bluer-objects/tree/main/bluer_objects/README), based on 🐬 [`bluer_ugv-7.819.1`](https://github.com/kamangir/bluer-ugv).
70
69
 
71
70
 
72
71
  built by 🌀 [`blueness-3.118.1`](https://github.com/kamangir/blueness).
@@ -1,7 +1,7 @@
1
- bluer_ugv/__init__.py,sha256=TQ0AauoKzZXCSZ5vQwGeiQZ84OdL-MLNeW1HAIL37mk,288
1
+ bluer_ugv/__init__.py,sha256=MF2_2KjvTzBbm2OHL9jTBV-BBc6437vlbYj5I1vZJmw,288
2
2
  bluer_ugv/__main__.py,sha256=77TquqyMca7qHk0XtCixGVyzfW3_9ywnl5oXEPERlls,374
3
- bluer_ugv/config.env,sha256=TODRtdZqUAYbVudsJgwu24IIJzOdd78FkYB59xjQNSo,1164
4
- bluer_ugv/env.py,sha256=f5Wb4Fb6136bZBkUkZJGxnwtA9NIGyORZCsjV0I1hTg,2193
3
+ bluer_ugv/config.env,sha256=Rm329eqF1ufQB6h73iIRpdSddWChbLpIEnRw0OMBDbc,1195
4
+ bluer_ugv/env.py,sha256=raTEobLhtz0krmzz56eNhKGS3nVkqjvRX5SvwLrOHWM,2261
5
5
  bluer_ugv/host.py,sha256=DpdJCvBpUfP9LkZgcH4BcdLGD-WZUvbTsjvMfPtS8uY,292
6
6
  bluer_ugv/logger.py,sha256=faY6GL6xfZAzlZ1aJ-MEHuPRJZNx8PYS4jMyY3xuxMw,98
7
7
  bluer_ugv/sample.env,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -9,12 +9,13 @@ bluer_ugv/urls.py,sha256=Bjdewssljt0LIefjixBem9JN0kkghPvmrIETj3GdXbY,17
9
9
  bluer_ugv/.abcli/abcli.sh,sha256=oaYgx1TCS6UWaYCO8__Mz2H5_zEN9dL3v_RjuwBAo0o,198
10
10
  bluer_ugv/.abcli/actions.sh,sha256=SqOmx12IOMopT2PgiHgAu5BwLgIi92I98LZ64dZSsXw,224
11
11
  bluer_ugv/.abcli/aka.sh,sha256=RHDU_JbEEL2B0vvvRJ3NVSsRSEjSu09jNY85n7DLe-k,21
12
- bluer_ugv/.abcli/alias.sh,sha256=AorjNOAzLMA5IH9HQLjh79VSj31LdTk4njse0NCS8-g,109
12
+ bluer_ugv/.abcli/alias.sh,sha256=WgSjjtC6rCqs7kChT-89dILSmp4f79R9iY3wYkRPSkY,77
13
13
  bluer_ugv/.abcli/bluer_UGV.sh,sha256=d1R6TQgIQugKeUK3RZbxgm-6d9QqkJwwoPCQKOC8Xb0,250
14
14
  bluer_ugv/.abcli/swallow.sh,sha256=b3LpA-OSCa7ykRtLnWDQYrqmOvMfoP15al90krLmb2I,319
15
15
  bluer_ugv/.abcli/swallow/dataset.sh,sha256=rHRTCShdP3Uls5ZmTKhRzQHX6a5bZvi8Cj1QDpO4tH4,343
16
16
  bluer_ugv/.abcli/swallow/debug.sh,sha256=JVjExhotePW3gZG2MUS6IzbXVQwMAefKYJiSP88wqlk,559
17
17
  bluer_ugv/.abcli/swallow/env.sh,sha256=5rKCdZhrQa_9Lr96j1h7XrWBcf0YEeV3w3o0vLWGd0M,360
18
+ bluer_ugv/.abcli/swallow/ethernet.sh,sha256=BiZNtRmDqTMT3A26fkvTPe6gO9o-KAGr7xM9WdXY9yA,365
18
19
  bluer_ugv/.abcli/swallow/git.sh,sha256=2S-dyqfMEE84m_E30LGx_UoJFMyToy0CPuBy_z5Ng6s,364
19
20
  bluer_ugv/.abcli/swallow/keyboard.sh,sha256=RYR5vgV0C1SATqekQ_F6Lr6Ums0IAkH9THX2ZPcitoY,402
20
21
  bluer_ugv/.abcli/swallow/select_target.sh,sha256=fwHmKbVcn_OEW4q-cjNc8Eyi6hOi1pMAb6x5tW8_lb0,183
@@ -29,6 +30,7 @@ bluer_ugv/.abcli/swallow/dataset/upload.sh,sha256=KM33QM4rIQpltbd_V49wPWear9Wi8c
29
30
  bluer_ugv/.abcli/swallow/env/cp.sh,sha256=vWBlG9n4Q6m_lBWcot9nyN6kc7-uil5skCjfJWHCfrY,168
30
31
  bluer_ugv/.abcli/swallow/env/list.sh,sha256=pT-hRS0ZfmogAfAMfm-hh0raGoUAnl66U5VuqgyeIwE,173
31
32
  bluer_ugv/.abcli/swallow/env/set.sh,sha256=ed6NCkhYK9LNhSYU_iJUNYXuyQFzmH9mydQ5agihhvA,817
33
+ bluer_ugv/.abcli/swallow/ethernet/test.sh,sha256=PWkJ7YTqxqmhIYdEdpHl1vs-VK90FRqisYjlKGglVdY,256
32
34
  bluer_ugv/.abcli/swallow/git/rm_keys.sh,sha256=jekY8r9olDq2ZH0OHHbbj9y39StUw9PWK0oLRIW87cg,870
33
35
  bluer_ugv/.abcli/swallow/keyboard/test.sh,sha256=B04AO-Er3_utzULPziyKD0aybuN2bry7h9wrQBEJ0tA,218
34
36
  bluer_ugv/.abcli/swallow/ultrasonic/review.sh,sha256=XoKwUliTcH-yTHrIo1cc9RSxL3tYQsBfWf1XOJ6YtOI,717
@@ -40,7 +42,7 @@ bluer_ugv/.abcli/swallow/video/playlist/download.sh,sha256=_HsWoW3rtLWMZpAxd2iem
40
42
  bluer_ugv/.abcli/swallow/video/playlist/edit.sh,sha256=_l-XpJOybxIkfcE797DBXwlcBcWSZqzTwIEGxk14-BY,403
41
43
  bluer_ugv/.abcli/swallow/video/playlist/upload.sh,sha256=8yGQtenNTNPQvifffED0FCP4Rjz0jHHvaDdcTv-vn9M,206
42
44
  bluer_ugv/.abcli/tests/README.sh,sha256=w1xjPWgCfWbLtON_LEn2cvSmPLulzm7kTBPEC3LJixs,142
43
- bluer_ugv/.abcli/tests/help.sh,sha256=uZ22au2-qHJMAIOLNT9ZYXNkjrJaCl9yxXM7BOJ98Bw,1590
45
+ bluer_ugv/.abcli/tests/help.sh,sha256=T99APdWB491sx1SVQjKCuNK8vvKuFBEu8Kvq2YZKuhc,1630
44
46
  bluer_ugv/.abcli/tests/swallow_dataset_combine.sh,sha256=tXjZj5iOOmCz3ito9lBDYjaAvR2odNzs2LRXsrM6d6c,291
45
47
  bluer_ugv/.abcli/tests/swallow_dataset_combine_explicit.sh,sha256=Ff4V7fi-h1myRT9ajjaKXesNrpaxLZMGDidBT8G31Kw,360
46
48
  bluer_ugv/.abcli/tests/swallow_dataset_combine_sequence.sh,sha256=N3Iq-zJV2OfEoft44D1BBLOlrkAbUDN_Rp_sAmjCEVk,311
@@ -57,14 +59,14 @@ bluer_ugv/.abcli/ugv/git.sh,sha256=U5p_5qrNwLw8O7HfHu7QZgLblbDc6Z6lIumgKwu3mzY,2
57
59
  bluer_ugv/.abcli/ugv/ssh.sh,sha256=G-pTQtMBcLmEJMhSoFKOE6a06tdylTd8c4eyB-lrnHY,506
58
60
  bluer_ugv/.abcli/ugv/watch.sh,sha256=5N7LEVAWOmmWfPGSiOThkqO9uYTXIrfm30nZjSfy72I,514
59
61
  bluer_ugv/README/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
- bluer_ugv/README/alias.py,sha256=jVRedHJRsoiauqcsN_APP1XgdXHsQhAsTQcMV4mHqEw,144
62
+ bluer_ugv/README/aliases.py,sha256=Og6Dvg6IIyrLUdllSIWIfosX0BhCmPw4F1e6liBYjzs,440
61
63
  bluer_ugv/README/beast.py,sha256=6sOpm4vNXxNtNMZszgiuqqJaBBWG02WaSgKfbdt7J4E,57
62
64
  bluer_ugv/README/build.py,sha256=xxUHeDMamCHLA33ZyGyowNGL2gVO0OJzAkNhIA9kdug,994
63
65
  bluer_ugv/README/consts.py,sha256=bQ98yk28E8-EBh2wUgMNr-sZ90IC3q7BmTzJe5XCS3g,257
64
- bluer_ugv/README/docs.py,sha256=kZKT5diLUFIzZqqP7do23v0ZA4hja1v5-L5LCL3UIxg,799
66
+ bluer_ugv/README/docs.py,sha256=tfb1_kWi0_dSr6bix7BiuphNaO75dl-wOtUo_TbmiOY,803
65
67
  bluer_ugv/README/items.py,sha256=-CZroToMKNHuqU9QugCfZXxH9DldPoVxdKasQ8Ywfvg,2229
66
68
  bluer_ugv/README/releases.py,sha256=T8SSJGq7kR902BspgcZlgEIlBa0tx3qNVfe9iRTcFOM,172
67
- bluer_ugv/README/root.py,sha256=h1piDAmlkdfbKZeZTW3HWzsW4VpKG6L776oYK7hC-J0,296
69
+ bluer_ugv/README/root.py,sha256=Z9Om4Fc5k8_zbzwp67awWiresauqb3D-hh4JseKcWlU,428
68
70
  bluer_ugv/README/shortcuts.py,sha256=vjE1QUdZg4omCoqy3BoTXQUbe6c5ERyq2Hjbl5TDw28,1316
69
71
  bluer_ugv/README/arzhang/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
72
  bluer_ugv/README/arzhang/algo.py,sha256=om7gokRGJ5X_yMi40r9SgoNav3C4Pcd7S7TuQv58ccs,134
@@ -118,15 +120,17 @@ bluer_ugv/README/swallow/digital/algo/driving.py,sha256=kr-uGXePnizPAb6ceajC9ToZ
118
120
  bluer_ugv/README/swallow/digital/algo/navigation.py,sha256=Hg_tkGYnE8qbM37Go4KlAaO-sNAtOBmaNZlPda45BCc,1780
119
121
  bluer_ugv/README/swallow/digital/algo/tracking.py,sha256=hHvAAIeVZ7Qlqm09_oHoQoSnp00l6rtYnnJqV8XfFUo,1568
120
122
  bluer_ugv/README/swallow/digital/algo/yolo.py,sha256=NlDISZ9LOUmMAzdEYzVYOrIwbDGdM8euMURlBOGACUk,930
121
- bluer_ugv/README/swallow/digital/design/__init__.py,sha256=VP_oK0MQ5yPl_FfbY9mLWNTvdi1amsau5Jkjse6a_Es,1991
123
+ bluer_ugv/README/swallow/digital/design/__init__.py,sha256=ZHG3ia8khbbwN1rSvmNkf_4r_zCStomnxpyUzYTAgjQ,2025
124
+ bluer_ugv/README/swallow/digital/design/ethernet.py,sha256=ZPu-JYsiiNpiXC3gkRShU6bNeYjzbkWtdR_jtxVc-pA,86
122
125
  bluer_ugv/README/swallow/digital/design/mechanical.py,sha256=OwgMrFIpRA-RR9CwurXaif09GCd6_i3zGuAfkH0jtsM,983
123
126
  bluer_ugv/README/swallow/digital/design/parts.py,sha256=R_CePkG5zF3qn1X4hvQyd8Iunc0u6yfWXyJJZi3fWBw,549
124
127
  bluer_ugv/README/swallow/digital/design/ultrasonic_sensor.py,sha256=VefIdTUXnsxVj3rmjsguV7N2BD9tPRiiaHjjRl84QUM,1517
125
128
  bluer_ugv/README/ugvs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
- bluer_ugv/README/ugvs/__main__.py,sha256=4eIr40fS4QD0BkAMk_vo6dGYOUlbPjDs_cN-uoHlILs,857
127
- bluer_ugv/README/ugvs/db.py,sha256=GmB6hQyVVEHDKJ6Jcos8aLOUDor-wJgi28AXBZodBbQ,4776
129
+ bluer_ugv/README/ugvs/__main__.py,sha256=Vaq7Wfp8Ohy4bSIvUAtmzNvvgU26VTl6VHhrvKZ9eF4,704
130
+ bluer_ugv/README/ugvs/db.py,sha256=z-w4RhSZjrC_mgzRStN2K2amGnrfJWWSajJ6q65WZQE,4778
128
131
  bluer_ugv/README/ugvs/docs.py,sha256=q0I1ZPUJITg0G7dW5Lv8dnNm0JEL4rdMkcaD4qU3XRk,3420
129
- bluer_ugv/README/ugvs/get.py,sha256=fxxPlvr-A38iw6o_1izXdd82N0C5W6y45R7eODK_ujk,487
132
+ bluer_ugv/README/ugvs/ethernet.py,sha256=6MGGWW-RWbx8-59EJKrA0Ncmje72-lYC4PUawrRdAkI,1584
133
+ bluer_ugv/README/ugvs/get.py,sha256=WMeSYKFA-XKY_rYL2zGo4TiN503Kg_w-CyLeD5NyFZM,378
130
134
  bluer_ugv/README/ugvs/comparison/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
131
135
  bluer_ugv/README/ugvs/comparison/build.py,sha256=Dj86jYV0gGHZQrTRH0wZ_Q8eh3yREBx5bgQ1Sw2bahA,4634
132
136
  bluer_ugv/README/ugvs/comparison/features/DYI.py,sha256=ED5Tlc4KcEFpLPHjwfSNGE47guKCeBG7Q84TS0BPJgE,187
@@ -166,14 +170,15 @@ bluer_ugv/designs/ravin/ravin4/parts.py,sha256=Skgkut_OCRPUwtLX_gIe70A1n_r2-48aP
166
170
  bluer_ugv/help/__init__.py,sha256=ajz1GSNU9xYVrFEDSz6Xwg7amWQ_yvW75tQa1ZvRIWc,3
167
171
  bluer_ugv/help/__main__.py,sha256=ww3p0cIRZm-SDODZ_fYAzPV_-nucxtidjK1IP2idW8g,229
168
172
  bluer_ugv/help/functions.py,sha256=Bx8Qv1yZRi0SOadfUkcaZdLY3oPbJBSX3Iyr9UZ66To,570
169
- bluer_ugv/help/get.py,sha256=vpvdU27VppybdhfFjXJ-CDpkCwb3j0jfZuY7dxBOqSs,437
173
+ bluer_ugv/help/get.py,sha256=eGIBTeJqZxOSByRGMeOe7IBR9dwipvflemaKWtKvRA8,368
170
174
  bluer_ugv/help/git.py,sha256=4CcaA8lHoplrrFgF5lolwVT79SLr3gE2iqXpAIfzH64,319
171
175
  bluer_ugv/help/ssh.py,sha256=MpGHCgaCaOO_ALZ8UaFPlawg9CUngWe1rdA6xkcjbf0,351
172
176
  bluer_ugv/help/watch.py,sha256=1Xjet_lR7q1deEee7I0DZIS58HBn7AUoVAUpsE0l1Ns,355
173
- bluer_ugv/help/swallow/__init__.py,sha256=13rjEuN0KXX6PpYlKzG8Pf3H3TKS3U8f9AZBhqacfSA,842
177
+ bluer_ugv/help/swallow/__init__.py,sha256=EeNSYD6q3eAbtFBncc4jgMfsBXWr_MBhF0hJE6gteoI,949
174
178
  bluer_ugv/help/swallow/dataset.py,sha256=59D5XLQG1PRWfPwfM73Jz1-f0qh_cO0q18qLtsxXtj0,2495
175
179
  bluer_ugv/help/swallow/debug.py,sha256=70krqg-YtjxPjPwWnx4J1Fxlvw46hHyAYq7pA7yPt50,481
176
180
  bluer_ugv/help/swallow/env.py,sha256=0CZyXIMWV2cpV_VbgdSEXigc63UNvQ-L2tjb-5Edwe8,1531
181
+ bluer_ugv/help/swallow/ethernet.py,sha256=9ACYo8blwSNb0ZobU68h6oCfUu0vMRpsVHSH2Ddv4UE,571
177
182
  bluer_ugv/help/swallow/git.py,sha256=u_08uXk1OqL5oV9vdunldr_EWpxo_aA5LemvW-gp0cw,425
178
183
  bluer_ugv/help/swallow/keyboard.py,sha256=YNkGWvE7tN3agCaL2R30FSIF2zw6oXa5ZJdnTJQA3Hk,518
179
184
  bluer_ugv/help/swallow/select_target.py,sha256=s1-v2IyT6thAncIzo4wKnU8hFJ-rDyi-IDzx2kMaJrs,390
@@ -197,16 +202,22 @@ bluer_ugv/swallow/session/classical/mode.py,sha256=6rFe1IIpfijuI7Xd-wcGKhYQ7EdQx
197
202
  bluer_ugv/swallow/session/classical/mousepad.py,sha256=LI7yvmtrZZaGUgPGpxNU7zcPkW8D5ykHlY_O-4H2STs,1871
198
203
  bluer_ugv/swallow/session/classical/position.py,sha256=gNeNzNpUG8zkDrcwibxaYStPNKUN1_DV5RjHu_pHMIg,913
199
204
  bluer_ugv/swallow/session/classical/push_button.py,sha256=s8sPart_dECjLK-gfsJi0UorRYcFYf3ofLqjmG8PoA8,2397
200
- bluer_ugv/swallow/session/classical/session.py,sha256=TqOcVzqVkGwuv2VKX6jPbG_K2yKO5JhxDXvjjghktY8,6037
205
+ bluer_ugv/swallow/session/classical/session.py,sha256=tF0yj3KlJb_aeUjLAS_taVmzQu6ILqjYfOSP7mfHXKQ,6257
201
206
  bluer_ugv/swallow/session/classical/camera/__init__.py,sha256=2GchuT19_Ns0uYRS5aUMz4pBwbK3y5XoFxoAlAnJKh8,464
202
207
  bluer_ugv/swallow/session/classical/camera/generic.py,sha256=tW7bQMzmBi9MV28cTlKlDhFdtnB-9mkpBzjaWzEJNec,1220
203
208
  bluer_ugv/swallow/session/classical/camera/navigation.py,sha256=HoeTlifGEfnedb6o8P-O0TcP1bNDRJ0M7vWupQh1pL4,6790
204
209
  bluer_ugv/swallow/session/classical/camera/tracking.py,sha256=7pMweFUHZe3BETXm9fD4A1YnyJxqT7syDbZYPb2f4v4,4438
205
210
  bluer_ugv/swallow/session/classical/camera/void.py,sha256=IJkJQgTgVucTYagFIwKEchNf3Vcjl99EdCdSdvKaeJo,216
206
211
  bluer_ugv/swallow/session/classical/camera/yolo.py,sha256=i-lEsLpqlmm1v3sa5azQYk7LCgU9Me5y28-LSPurc7g,6412
212
+ bluer_ugv/swallow/session/classical/ethernet/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
213
+ bluer_ugv/swallow/session/classical/ethernet/__main__.py,sha256=7xSxV1qaxgOehQZn_lgVxemM43ZlJjKBmqPC-cQuXD4,846
214
+ bluer_ugv/swallow/session/classical/ethernet/classes.py,sha256=v8u41aFlzbq6_SHpxCxRY6eBzwmTsTxQiNuXtTRz4oQ,1301
215
+ bluer_ugv/swallow/session/classical/ethernet/client.py,sha256=jVf-JBrKt_r1k_GxhNOCYL-BjFVHASpKghLWglMQWBI,8593
216
+ bluer_ugv/swallow/session/classical/ethernet/command.py,sha256=ppS4Tkks4yLNsycAKdmZX6EfUEMThaJ2Il42dwdF__c,597
217
+ bluer_ugv/swallow/session/classical/ethernet/testing.py,sha256=SvLp12FqcJeph6z6jP1OR5h6cugcptTyP9t2AVHGTEs,1149
207
218
  bluer_ugv/swallow/session/classical/keyboard/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
208
219
  bluer_ugv/swallow/session/classical/keyboard/__main__.py,sha256=tdX-a8MC9ix6aQ9isZXfnIqa9SuiYGr73Yy05RwvdCs,603
209
- bluer_ugv/swallow/session/classical/keyboard/classes.py,sha256=6uN_aZqu6AcNibwi9rimkDRlTm5-LWuzVGzvy5YUpIY,4573
220
+ bluer_ugv/swallow/session/classical/keyboard/classes.py,sha256=l9kfV3sqBjlHNjoBsXDL0cOwHeNVD42KMzasoBUYd7w,5415
210
221
  bluer_ugv/swallow/session/classical/keyboard/keys.py,sha256=Z27XTC2agwC_uYmzBgRKP3hNy58KWnWUxHXeOChuooc,2348
211
222
  bluer_ugv/swallow/session/classical/keyboard/testing.py,sha256=GFh-6-HkT1sYlGt-Y2traBFD8lle3C-HcvVrdzsITEo,511
212
223
  bluer_ugv/swallow/session/classical/motor/__init__.py,sha256=Ja9pVS6h5wnZiDvJ2IaibLcleQl3inIF_YRJg9VGKlw,323
@@ -236,8 +247,8 @@ bluer_ugv/swallow/session/classical/ultrasonic_sensor/pack.py,sha256=bVcG8V0P6Sl
236
247
  bluer_ugv/swallow/session/classical/ultrasonic_sensor/review.py,sha256=_iQmec2VSq-ORkTVpXSNJg08hrSz2M1O8LzNZEtoT9U,959
237
248
  bluer_ugv/swallow/session/classical/ultrasonic_sensor/sensor.py,sha256=En-J8BRTiR8k9p2CBbW0cOUuw8nctD5DOXDm5uhCNO4,3987
238
249
  bluer_ugv/swallow/session/classical/ultrasonic_sensor/testing.py,sha256=yj2MY_Jo1FzeXnR_JoFAXsb9eHEVhicDiHEsUa8z03U,1525
239
- bluer_ugv-7.790.1.dist-info/licenses/LICENSE,sha256=ogEPNDSH0_dhiv_lT3ifVIdgIzHAqNA_SemnxUfPBJk,7048
240
- bluer_ugv-7.790.1.dist-info/METADATA,sha256=-bUIKkkMY4ywNrU7deElgpEWfS6nyaIvbrePvZafVg8,5707
241
- bluer_ugv-7.790.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
242
- bluer_ugv-7.790.1.dist-info/top_level.txt,sha256=_MlDhFvIPpher9Zs7xyJTHgO2xJJTbfR1dzncz3LQnk,10
243
- bluer_ugv-7.790.1.dist-info/RECORD,,
250
+ bluer_ugv-7.819.1.dist-info/licenses/LICENSE,sha256=ogEPNDSH0_dhiv_lT3ifVIdgIzHAqNA_SemnxUfPBJk,7048
251
+ bluer_ugv-7.819.1.dist-info/METADATA,sha256=q2xAvCO63UomDqmDKot6y5gwtueqHkS4v6oUSCrdE30,5612
252
+ bluer_ugv-7.819.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
253
+ bluer_ugv-7.819.1.dist-info/top_level.txt,sha256=_MlDhFvIPpher9Zs7xyJTHgO2xJJTbfR1dzncz3LQnk,10
254
+ bluer_ugv-7.819.1.dist-info/RECORD,,
bluer_ugv/README/alias.py DELETED
@@ -1,10 +0,0 @@
1
- docs = [
2
- {
3
- "path": f"../docs/aliases/{alias}",
4
- }
5
- for alias in [
6
- "",
7
- "swallow.md",
8
- "ugv.md",
9
- ]
10
- ]