Skip to content

Commit 7f178b3

Browse files
committed
Adds auto-configure for sim GC
1 parent 4406c35 commit 7f178b3

5 files changed

Lines changed: 204 additions & 1 deletion

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright 2026 A Team
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
# THE SOFTWARE.
20+
21+
22+
from .change_game_contoller_config import ChangeGameControllerConfig
23+
from .change_game_controller_team_name import ChangeGameControllerTeamName
24+
25+
26+
__all__ = [
27+
'ChangeGameControllerConfig',
28+
'ChangeGameControllerTeamName'
29+
]
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Copyright 2026 A Team
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
# THE SOFTWARE.
20+
21+
import launch
22+
from launch.some_substitutions_type import SomeSubstitutionsType
23+
from launch import LaunchContext, Substitution
24+
from launch.actions import OpaqueCoroutine
25+
from launch.utilities import normalize_to_list_of_substitutions
26+
from typing import Mapping, Union, Any
27+
28+
import asyncio
29+
import json
30+
import websockets
31+
32+
33+
class ChangeGameControllerConfig(OpaqueCoroutine):
34+
"""Action that sends config delta commands to the GC API"""
35+
36+
def __init__(self, configs: Mapping[Union[Substitution, str], Any], gc_address: SomeSubstitutionsType) -> None:
37+
"""Initialize the action."""
38+
super().__init__(coroutine=self.my_coroutine)
39+
self.__configs = configs
40+
self.__gc_address = normalize_to_list_of_substitutions(gc_address)
41+
42+
async def my_coroutine(self, context: LaunchContext, *args, **kwargs):
43+
gc_address = context.perform_substitution(self.__gc_address[0])
44+
config_delta = {}
45+
for k, v in self.__configs.items():
46+
if isinstance(k, Substitution):
47+
key = context.perform_substitution(k)
48+
else:
49+
key = k
50+
if isinstance(v, Substitution):
51+
value = context.perform_substitution(v)
52+
else:
53+
value = v
54+
config_delta[key] = value
55+
await self.send_json_payload(gc_address, config_delta)
56+
57+
async def send_json_payload(self, address: str, configs: Mapping[str, str]):
58+
payload = {
59+
'config_delta': configs
60+
}
61+
server_url = f'ws://{address}:8081/api/control'
62+
while True:
63+
try:
64+
async with websockets.connect(server_url) as ws:
65+
await ws.send(json.dumps(payload))
66+
# await ws.recv()
67+
break
68+
except ConnectionRefusedError:
69+
continue
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Copyright 2026 A Team
2+
#
3+
# Permission is hereby granted, free of charge, to any person obtaining a copy
4+
# of this software and associated documentation files (the "Software"), to deal
5+
# in the Software without restriction, including without limitation the rights
6+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
# copies of the Software, and to permit persons to whom the Software is
8+
# furnished to do so, subject to the following conditions:
9+
#
10+
# The above copyright notice and this permission notice shall be included in
11+
# all copies or substantial portions of the Software.
12+
#
13+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16+
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
# THE SOFTWARE.
20+
21+
import launch
22+
from launch.some_substitutions_type import SomeSubstitutionsType
23+
from launch import LaunchContext, Substitution
24+
from launch.actions import OpaqueCoroutine
25+
from launch.utilities import normalize_to_list_of_substitutions
26+
from typing import Mapping, Union, Any
27+
28+
import asyncio
29+
import json
30+
import websockets
31+
32+
33+
class ChangeGameControllerTeamName(OpaqueCoroutine):
34+
"""Action that sends config delta commands to the GC API"""
35+
36+
def __init__(self, color: Union[Substitution, str], name: Union[Substitution, str], gc_address: SomeSubstitutionsType) -> None:
37+
"""Initialize the action."""
38+
super().__init__(coroutine=self.my_coroutine)
39+
self.__color = color
40+
self.__team_name = name
41+
self.__gc_address = normalize_to_list_of_substitutions(gc_address)
42+
43+
async def my_coroutine(self, context: LaunchContext, *args, **kwargs):
44+
gc_address = context.perform_substitution(self.__gc_address[0])
45+
color = ''
46+
if isinstance(self.__color, Substitution):
47+
color = context.perform_substitution(self.__color)
48+
else:
49+
color = self.__color
50+
color = color.upper()
51+
team_name = ''
52+
if isinstance(self.__team_name, Substitution):
53+
team_name = context.perform_substitution(self.__team_name)
54+
else:
55+
team_name = self.__team_name
56+
57+
launch.logging.get_logger().info(f'Color: {color} Name: {team_name}')
58+
59+
await self.send_json_payload(gc_address, color, team_name)
60+
61+
async def send_json_payload(self, address: str, color: str, name: str):
62+
payload = {
63+
'change': {
64+
'origin': 'UI',
65+
'revertible': True,
66+
'update_team_state_change': {
67+
'for_team': color,
68+
'team_name': name
69+
}
70+
}
71+
}
72+
server_url = f'ws://{address}:8081/api/control'
73+
launch.logging.get_logger().info(f'Sending: {json.dumps(payload)}')
74+
while True:
75+
try:
76+
async with websockets.connect(server_url) as ws:
77+
await ws.send(json.dumps(payload))
78+
# await ws.recv()
79+
break
80+
except ConnectionRefusedError:
81+
continue

ateam_bringup/launch/bringup_simulation.launch.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,12 @@
2323
PackageLaunchFileSubstitution
2424
)
2525
import launch
26-
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, LogInfo
26+
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, LogInfo, GroupAction
2727
from launch.conditions import IfCondition, UnlessCondition
2828
from launch.launch_description_sources import FrontendLaunchDescriptionSource
2929
from launch.substitutions import LaunchConfiguration
3030
from launch_ros.actions import Node
31+
from ateam_bringup.actions import ChangeGameControllerTeamName, ChangeGameControllerConfig
3132

3233

3334
def generate_launch_description():
@@ -40,6 +41,8 @@ def generate_launch_description():
4041
DeclareLaunchArgument('gc_ip', default_value='172.17.0.2'),
4142
DeclareLaunchArgument('team_name', default_value='A-Team'),
4243
DeclareLaunchArgument('no_kenobi', default_value='False'),
44+
DeclareLaunchArgument('blue_team', default_value='A-Team'),
45+
DeclareLaunchArgument('yellow_team', default_value='RoboJackets'),
4346

4447
IncludeLaunchDescription(
4548
FrontendLaunchDescriptionSource(
@@ -51,6 +54,25 @@ def generate_launch_description():
5154
condition=IfCondition(LaunchConfiguration('start_sim'))
5255
),
5356

57+
GroupAction(
58+
actions=[
59+
IncludeLaunchDescription(
60+
FrontendLaunchDescriptionSource(
61+
PackageLaunchFileSubstitution('ateam_bringup',
62+
'ssl_game_controller.launch.xml'))
63+
),
64+
ChangeGameControllerConfig(gc_address='172.17.0.2', configs={
65+
'autoContinue': False
66+
}),
67+
ChangeGameControllerTeamName(
68+
gc_address='172.17.0.2', color='blue', name=LaunchConfiguration('blue_team')),
69+
ChangeGameControllerTeamName(
70+
gc_address='172.17.0.2', color='yellow', name=LaunchConfiguration('yellow_team')),
71+
72+
],
73+
condition=IfCondition(LaunchConfiguration('start_gc'))
74+
),
75+
5476
IncludeLaunchDescription(
5577
FrontendLaunchDescriptionSource(
5678
PackageLaunchFileSubstitution('ateam_bringup',

ateam_bringup/package.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
<exec_depend>docker.io</exec_depend>
1414

15+
<depend>python3-websockets</depend>
16+
1517
<test_depend>ament_lint_auto</test_depend>
1618
<test_depend>ament_lint_common</test_depend>
1719

0 commit comments

Comments
 (0)