Skip to content

Commit 8f5ec0b

Browse files
authored
Merge pull request #94 from adf-python/feature/center
司令塔の実装
2 parents 2f4ccbe + 4c933de commit 8f5ec0b

19 files changed

+1869
-37
lines changed

adf_core_python/core/agent/module/module_manager.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
from typing import TYPE_CHECKING, Any, Optional
55

66
from adf_core_python.core.component.action.extend_action import ExtendAction
7+
from adf_core_python.core.component.centralized.command_executor import CommandExecutor
8+
from adf_core_python.core.component.centralized.command_picker import CommandPicker
79
from adf_core_python.core.component.communication.channel_subscriber import (
810
ChannelSubscriber,
911
)
@@ -189,6 +191,58 @@ def get_message_coordinator(
189191
f"Message coordinator {class_name} is not a subclass of MessageCoordinator"
190192
)
191193

194+
def get_command_executor(
195+
self, command_executor_name: str, default_command_executor_name: str
196+
) -> CommandExecutor:
197+
class_name = self._module_config.get_value_or_default(
198+
command_executor_name, default_command_executor_name
199+
)
200+
201+
command_executor_class: type = self._load_module(class_name)
202+
203+
instance = self._executors.get(command_executor_name)
204+
if instance is not None:
205+
return instance
206+
207+
if issubclass(command_executor_class, CommandExecutor):
208+
instance = command_executor_class(
209+
self._agent_info,
210+
self._world_info,
211+
self._scenario_info,
212+
self,
213+
self._develop_data,
214+
)
215+
self._executors[command_executor_name] = instance
216+
return instance
217+
218+
raise RuntimeError(f"Command executor {class_name} is not a subclass of object")
219+
220+
def get_command_picker(
221+
self, command_picker_name: str, default_command_picker_name: str
222+
) -> CommandPicker:
223+
class_name = self._module_config.get_value_or_default(
224+
command_picker_name, default_command_picker_name
225+
)
226+
227+
command_picker_class: type = self._load_module(class_name)
228+
229+
instance = self._pickers.get(command_picker_name)
230+
if instance is not None:
231+
return instance
232+
233+
if issubclass(command_picker_class, CommandPicker):
234+
instance = command_picker_class(
235+
self._agent_info,
236+
self._world_info,
237+
self._scenario_info,
238+
self,
239+
self._develop_data,
240+
)
241+
self._pickers[command_picker_name] = instance
242+
return instance
243+
244+
raise RuntimeError(f"Command picker {class_name} is not a subclass of object")
245+
192246
def _load_module(self, class_name: str) -> type:
193247
module_name, module_class_name = class_name.rsplit(".", 1)
194248
module = importlib.import_module(module_name)
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
from __future__ import annotations
2+
3+
from abc import ABC, abstractmethod
4+
from typing import TYPE_CHECKING, Generic, Optional, TypeVar
5+
6+
if TYPE_CHECKING:
7+
from adf_core_python.core.agent.communication.message_manager import MessageManager
8+
from adf_core_python.core.agent.develop.develop_data import DevelopData
9+
from adf_core_python.core.agent.info.agent_info import AgentInfo
10+
from adf_core_python.core.agent.info.scenario_info import ScenarioInfo
11+
from adf_core_python.core.agent.info.world_info import WorldInfo
12+
from adf_core_python.core.agent.module.module_manager import ModuleManager
13+
from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData
14+
15+
from adf_core_python.core.agent.action.action import Action
16+
from adf_core_python.core.component.communication.communication_message import (
17+
CommunicationMessage,
18+
)
19+
20+
T = TypeVar("T", bound=CommunicationMessage)
21+
22+
23+
class CommandExecutor(ABC, Generic[T]):
24+
def __init__(
25+
self,
26+
agent_info: AgentInfo,
27+
world_info: WorldInfo,
28+
scenario_info: ScenarioInfo,
29+
module_manager: ModuleManager,
30+
develop_data: DevelopData,
31+
) -> None:
32+
self._agent_info = agent_info
33+
self._world_info = world_info
34+
self._scenario_info = scenario_info
35+
self._module_manager = module_manager
36+
self._develop_data = develop_data
37+
38+
self._result: Optional[Action] = None
39+
40+
self._count_precompute: int = 0
41+
self._count_prepare: int = 0
42+
self._count_resume: int = 0
43+
self._count_update_info: int = 0
44+
self._count_update_info_current_time: int = 0
45+
46+
@abstractmethod
47+
def set_command(self, command: T) -> CommandExecutor:
48+
pass
49+
50+
@abstractmethod
51+
def calculate(self) -> CommandExecutor:
52+
pass
53+
54+
def get_action(self) -> Optional[Action]:
55+
return self._result
56+
57+
def precompute(self, precompute_data: PrecomputeData) -> CommandExecutor:
58+
self._count_precompute += 1
59+
return self
60+
61+
def prepare(self) -> CommandExecutor:
62+
self._count_prepare += 1
63+
return self
64+
65+
def resume(self, precompute_data: PrecomputeData) -> CommandExecutor:
66+
self._count_resume += 1
67+
return self
68+
69+
def update_info(self, message_manager: MessageManager) -> CommandExecutor:
70+
if self._count_update_info_current_time != self._agent_info.get_time():
71+
self._count_update_info_current_time = self._agent_info.get_time()
72+
self._count_update_info += 1
73+
return self
74+
75+
def get_count_precompute(self) -> int:
76+
return self._count_precompute
77+
78+
def get_count_prepare(self) -> int:
79+
return self._count_prepare
80+
81+
def get_count_resume(self) -> int:
82+
return self._count_resume
83+
84+
def get_count_update_info(self) -> int:
85+
return self._count_update_info
86+
87+
def reset_count_precompute(self) -> None:
88+
self._count_precompute = 0
89+
90+
def reset_count_prepare(self) -> None:
91+
self._count_prepare = 0
92+
93+
def reset_count_resume(self) -> None:
94+
self._count_resume = 0
95+
96+
def reset_count_update_info(self) -> None:
97+
self._count_update_info = 0
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
from __future__ import annotations
2+
3+
from abc import ABC, abstractmethod
4+
from typing import TYPE_CHECKING
5+
6+
from rcrs_core.worldmodel.entityID import EntityID
7+
8+
if TYPE_CHECKING:
9+
from adf_core_python.core.agent.develop.develop_data import DevelopData
10+
from adf_core_python.core.agent.info.agent_info import AgentInfo
11+
from adf_core_python.core.agent.info.scenario_info import ScenarioInfo
12+
from adf_core_python.core.agent.info.world_info import WorldInfo
13+
from adf_core_python.core.agent.module.module_manager import ModuleManager
14+
15+
from adf_core_python.core.agent.communication.message_manager import MessageManager
16+
from adf_core_python.core.agent.precompute.precompute_data import PrecomputeData
17+
from adf_core_python.core.component.communication.communication_message import (
18+
CommunicationMessage,
19+
)
20+
21+
22+
class CommandPicker(ABC):
23+
def __init__(
24+
self,
25+
agent_info: AgentInfo,
26+
world_info: WorldInfo,
27+
scenario_info: ScenarioInfo,
28+
module_manager: ModuleManager,
29+
develop_data: DevelopData,
30+
) -> None:
31+
self._agent_info = agent_info
32+
self._world_info = world_info
33+
self._scenario_info = scenario_info
34+
self._module_manager = module_manager
35+
self._develop_data = develop_data
36+
self._count_precompute: int = 0
37+
self._count_prepare: int = 0
38+
self._count_resume: int = 0
39+
self._count_update_info: int = 0
40+
self._count_update_info_current_time: int = 0
41+
42+
@abstractmethod
43+
def set_allocator_result(
44+
self, allocation_data: dict[EntityID, EntityID]
45+
) -> CommandPicker:
46+
pass
47+
48+
@abstractmethod
49+
def calculate(self) -> CommandPicker:
50+
pass
51+
52+
@abstractmethod
53+
def get_result(self) -> list[CommunicationMessage]:
54+
pass
55+
56+
def precompute(self, precompute_data: PrecomputeData) -> CommandPicker:
57+
self._count_precompute += 1
58+
return self
59+
60+
def prepare(self) -> CommandPicker:
61+
self._count_prepare += 1
62+
return self
63+
64+
def resume(self, precompute_data: PrecomputeData) -> CommandPicker:
65+
self._count_resume += 1
66+
return self
67+
68+
def update_info(self, message_manager: MessageManager) -> CommandPicker:
69+
if self._count_update_info_current_time != self._agent_info.get_time():
70+
self._count_update_info_current_time = self._agent_info.get_time()
71+
self._count_update_info = 0
72+
self._count_update_info += 1
73+
return self
74+
75+
def get_count_precompute(self) -> int:
76+
return self._count_precompute
77+
78+
def get_count_prepare(self) -> int:
79+
return self._count_prepare
80+
81+
def get_count_resume(self) -> int:
82+
return self._count_resume
83+
84+
def get_count_update_info(self) -> int:
85+
return self._count_update_info
86+
87+
def get_count_update_info_current_time(self) -> int:
88+
return self._count_update_info_current_time
89+
90+
def reset_count_precompute(self) -> None:
91+
self._count_precompute = 0
92+
93+
def reset_count_prepare(self) -> None:
94+
self._count_prepare = 0
95+
96+
def reset_count_resume(self) -> None:
97+
self._count_resume = 0
98+
99+
def reset_count_update_info(self) -> None:
100+
self._count_update_info = 0

adf_core_python/core/component/tactics/tactics_center.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from abc import ABC, abstractmethod
44
from typing import TYPE_CHECKING, Any, Optional
55

6+
from adf_core_python.core.component.centralized.command_picker import CommandPicker
7+
68
if TYPE_CHECKING:
79
from adf_core_python.core.agent.communication.message_manager import MessageManager
810
from adf_core_python.core.agent.develop.develop_data import DevelopData
@@ -18,7 +20,7 @@ class TacticsCenter(ABC):
1820
def __init__(self, parent: Optional[TacticsCenter] = None) -> None:
1921
self._parent = parent
2022
self._modules: list[AbstractModule] = []
21-
self._command_pickers: list[Any] = []
23+
self._command_pickers: list[CommandPicker] = []
2224

2325
@abstractmethod
2426
def initialize(

0 commit comments

Comments
 (0)