bluer-ugv 7.790.1__py3-none-any.whl → 7.821.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.
- bluer_ugv/.abcli/alias.sh +0 -2
- bluer_ugv/.abcli/swallow/ethernet/test.sh +12 -0
- bluer_ugv/.abcli/swallow/ethernet.sh +15 -0
- bluer_ugv/.abcli/tests/help.sh +2 -0
- bluer_ugv/README/aliases.py +23 -0
- bluer_ugv/README/docs.py +2 -2
- bluer_ugv/README/root.py +4 -0
- bluer_ugv/README/swallow/digital/design/__init__.py +2 -0
- bluer_ugv/README/swallow/digital/design/ethernet.py +5 -0
- bluer_ugv/README/ugvs/__main__.py +0 -7
- bluer_ugv/README/ugvs/db.py +1 -1
- bluer_ugv/README/ugvs/ethernet.py +55 -0
- bluer_ugv/README/ugvs/get.py +0 -4
- bluer_ugv/__init__.py +1 -1
- bluer_ugv/config.env +5 -1
- bluer_ugv/env.py +7 -0
- bluer_ugv/help/get.py +1 -6
- bluer_ugv/help/swallow/__init__.py +2 -0
- bluer_ugv/help/swallow/ethernet.py +34 -0
- bluer_ugv/swallow/session/classical/camera/yolo.py +7 -0
- bluer_ugv/swallow/session/classical/ethernet/__init__.py +0 -0
- bluer_ugv/swallow/session/classical/ethernet/__main__.py +42 -0
- bluer_ugv/swallow/session/classical/ethernet/classes.py +52 -0
- bluer_ugv/swallow/session/classical/ethernet/client.py +278 -0
- bluer_ugv/swallow/session/classical/ethernet/command.py +22 -0
- bluer_ugv/swallow/session/classical/ethernet/testing.py +52 -0
- bluer_ugv/swallow/session/classical/keyboard/classes.py +19 -0
- bluer_ugv/swallow/session/classical/session.py +6 -0
- {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.821.1.dist-info}/METADATA +4 -5
- {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.821.1.dist-info}/RECORD +33 -22
- bluer_ugv/README/alias.py +0 -10
- {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.821.1.dist-info}/WHEEL +0 -0
- {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.821.1.dist-info}/licenses/LICENSE +0 -0
- {bluer_ugv-7.790.1.dist-info → bluer_ugv-7.821.1.dist-info}/top_level.txt +0 -0
bluer_ugv/.abcli/alias.sh
CHANGED
|
@@ -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
|
bluer_ugv/.abcli/tests/help.sh
CHANGED
|
@@ -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
|
-
|
|
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
|
-
+
|
|
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
|
|
@@ -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:
|
bluer_ugv/README/ugvs/db.py
CHANGED
|
@@ -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, ""
|
bluer_ugv/README/ugvs/get.py
CHANGED
|
@@ -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
bluer_ugv/config.env
CHANGED
|
@@ -36,4 +36,8 @@ 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
|
|
42
|
+
|
|
43
|
+
BLUER_UGV_SWALLOW_YOLO_SPEED_SETPOINT=20
|
bluer_ugv/env.py
CHANGED
|
@@ -40,6 +40,11 @@ BLUER_UGV_SWALLOW_STEERING_YOLO_EXPIRY = get_env(
|
|
|
40
40
|
0.1,
|
|
41
41
|
)
|
|
42
42
|
|
|
43
|
+
BLUER_UGV_SWALLOW_YOLO_SPEED_SETPOINT = get_env(
|
|
44
|
+
"BLUER_UGV_SWALLOW_YOLO_SPEED_SETPOINT",
|
|
45
|
+
20,
|
|
46
|
+
)
|
|
47
|
+
|
|
43
48
|
BLUER_UGV_SWALLOW_YOLO_DATASET_LIST = get_env(
|
|
44
49
|
"BLUER_UGV_SWALLOW_YOLO_DATASET_LIST",
|
|
45
50
|
)
|
|
@@ -105,3 +110,5 @@ BLUER_UGV_AUDIO_LENGTH = get_env(
|
|
|
105
110
|
"BLUER_UGV_AUDIO_LENGTH",
|
|
106
111
|
30,
|
|
107
112
|
)
|
|
113
|
+
|
|
114
|
+
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
|
+
}
|
|
@@ -156,6 +156,7 @@ class ClassicalYoloCamera(ClassicalCamera):
|
|
|
156
156
|
logger.warning("failed to send debug data.")
|
|
157
157
|
|
|
158
158
|
if not metadata["detections"]:
|
|
159
|
+
self.setpoint.stop()
|
|
159
160
|
logger.info("no detections.")
|
|
160
161
|
return True
|
|
161
162
|
|
|
@@ -177,6 +178,12 @@ class ClassicalYoloCamera(ClassicalCamera):
|
|
|
177
178
|
steering_expires_in=env.BLUER_UGV_SWALLOW_STEERING_YOLO_EXPIRY,
|
|
178
179
|
)
|
|
179
180
|
|
|
181
|
+
self.setpoint.put(
|
|
182
|
+
what="speed",
|
|
183
|
+
value=env.BLUER_UGV_SWALLOW_YOLO_SPEED_SETPOINT,
|
|
184
|
+
log=True,
|
|
185
|
+
)
|
|
186
|
+
|
|
180
187
|
return True
|
|
181
188
|
|
|
182
189
|
def loop_training(self) -> bool:
|
|
File without changes
|
|
@@ -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.
|
|
3
|
+
Version: 7.821.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
|
-
[@
|
|
57
|
-
[@
|
|
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
|
[](https://github.com/kamangir/bluer-ugv/actions/workflows/pylint.yml) [](https://github.com/kamangir/bluer-ugv/actions/workflows/pytest.yml) [](https://github.com/kamangir/bluer-ugv/actions/workflows/bashtest.yml) [](https://pypi.org/project/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.
|
|
68
|
+
built by 🌀 [`bluer README`](https://github.com/kamangir/bluer-objects/tree/main/bluer_objects/README), based on 🐬 [`bluer_ugv-7.821.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=
|
|
1
|
+
bluer_ugv/__init__.py,sha256=f8QgN1CUaC6oG1iLYBElPp-7hfMOhWACtpK05wnlgNs,288
|
|
2
2
|
bluer_ugv/__main__.py,sha256=77TquqyMca7qHk0XtCixGVyzfW3_9ywnl5oXEPERlls,374
|
|
3
|
-
bluer_ugv/config.env,sha256=
|
|
4
|
-
bluer_ugv/env.py,sha256=
|
|
3
|
+
bluer_ugv/config.env,sha256=29E09ffL9mJQr3J9J_Xi5KoIiOxqDF1akquddhz4zrE,1237
|
|
4
|
+
bluer_ugv/env.py,sha256=dxTqlES0xLLooYDWcJDM7oOEJsOBE9LScHNtVjInvWQ,2366
|
|
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=
|
|
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=
|
|
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/
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
127
|
-
bluer_ugv/README/ugvs/db.py,sha256=
|
|
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/
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
-
bluer_ugv/swallow/session/classical/camera/yolo.py,sha256=
|
|
211
|
+
bluer_ugv/swallow/session/classical/camera/yolo.py,sha256=vStqH-cQbi0Rij1OqETv60QfMgSHz_9X9DaqKDPHNcI,6592
|
|
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=
|
|
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.
|
|
240
|
-
bluer_ugv-7.
|
|
241
|
-
bluer_ugv-7.
|
|
242
|
-
bluer_ugv-7.
|
|
243
|
-
bluer_ugv-7.
|
|
250
|
+
bluer_ugv-7.821.1.dist-info/licenses/LICENSE,sha256=ogEPNDSH0_dhiv_lT3ifVIdgIzHAqNA_SemnxUfPBJk,7048
|
|
251
|
+
bluer_ugv-7.821.1.dist-info/METADATA,sha256=LCz6r3hTAV67GTmIT2HKhn3v_PP-LoYmvPaT-Qq6GIo,5612
|
|
252
|
+
bluer_ugv-7.821.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
253
|
+
bluer_ugv-7.821.1.dist-info/top_level.txt,sha256=_MlDhFvIPpher9Zs7xyJTHgO2xJJTbfR1dzncz3LQnk,10
|
|
254
|
+
bluer_ugv-7.821.1.dist-info/RECORD,,
|
bluer_ugv/README/alias.py
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|